From 1bfaf1837c75273470516c9619e8b89a4e3db919 Mon Sep 17 00:00:00 2001 From: CyberSecurityUP Date: Fri, 2 Oct 2026 19:01:32 -0300 Subject: [PATCH] fix(pool): emit the quota/auth PAUSE notice once, not once per parallel agent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A single out-of-credit event (Anthropic's 400 "credit balance too low", which is_exhaustion correctly classifies) made every in-flight parallel agent print its own "PAUSED — /continue" line, flooding the console with identical notices. Now only the agent that wins the paused false->true transition emits the notice; the rest park quietly. Resume clears the flag so the next episode notices again. Co-Authored-By: Claude Opus 4.8 --- neurosploit-rs/crates/harness/src/pool.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/neurosploit-rs/crates/harness/src/pool.rs b/neurosploit-rs/crates/harness/src/pool.rs index e3e5c4d..e6f28fd 100644 --- a/neurosploit-rs/crates/harness/src/pool.rs +++ b/neurosploit-rs/crates/harness/src/pool.rs @@ -229,7 +229,16 @@ impl ModelPool { /// emit a notice, and wait until the user runs `/continue` (or cancels). /// Returns when the run should retry (pause cleared) or give up (cancelled). async fn park_exhausted(&self, err: &anyhow::Error, is_auth: bool) { - self.paused.store(true, Ordering::Relaxed); + // Only the agent that actually flips the run into the paused state + // emits the notice. Without this, every in-flight parallel agent that + // hits the same exhaustion prints its own "PAUSED — /continue" line, so + // a single out-of-credit event floods the console with identical + // notices. The losers of the transition just wait quietly below. + let first = self + .paused + .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) + .is_ok(); + if first { if let Some(tx) = self.progress() { let msg = format!("{err:#}"); let short = msg.lines().next().unwrap_or(&msg); @@ -247,6 +256,7 @@ impl ModelPool { }; let _ = tx.send(notice).await; } + } while self.paused.load(Ordering::Relaxed) && !self.is_cancelled() { let notified = self.resume.notified(); tokio::select! {