From 54c63307dcb367489cf311c295d8159acf5d08f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 11 Jul 2026 12:08:31 +0000 Subject: [PATCH] agent: report pipeline death to the polling agent comrak-010's patch added an unresolvable dependency version; the candidate build failed before the verdict binary ran, no ledger row was ever written, and the agent polled read_verdict into its turn cap. The detached pipeline now traps any failure into a log marker, and read_verdict reports status=failed with the log tail instead of 'running' forever. Attempt relaunched. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EcKPWQ3MQD4NdEHwar96u2 --- crates/harnessd/src/main.rs | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/harnessd/src/main.rs b/crates/harnessd/src/main.rs index 178307e..7192b39 100644 --- a/crates/harnessd/src/main.rs +++ b/crates/harnessd/src/main.rs @@ -353,10 +353,13 @@ fn handle(root: &Path, line: &str) -> Result { let cand_dir = format!("targets/{t}/candidate-{patch_id}"); let cand_bin = format!("{cand_dir}/release/{bin}"); let log = format!("results/pending/{run_id}.log"); + // On any failure (candidate build, gates crash) the trap line + // marks the log so read_verdict can report "failed" instead of + // leaving the agent polling a verdict that will never exist. let script = format!( - "set -e\nCARGO_TARGET_DIR={cand} {build}\ncargo run -q -p verdict -- {tgt} \ + "{{ set -e\nCARGO_TARGET_DIR={cand} {build}\ncargo run -q -p verdict -- {tgt} \ --rebuild-baseline --candidate-bin {cbin} --run-id {rid} --playbook-class {cls} \ - --hypothesis {hyp} --hotspot {hs} --patch-file {pf}\n", + --hypothesis {hyp} --hotspot {hs} --patch-file {pf}\n}} || echo HOTPATH-PIPELINE-FAILED\n", cand = shq(&cand_dir), build = spec.build.baseline, tgt = shq(t), @@ -392,6 +395,34 @@ fn handle(root: &Path, line: &str) -> Result { return Ok(v); } drop(ledger); + // A pipeline that died before the verdict binary ran never + // writes a row; its trap line marks the log instead. + if run_id + .chars() + .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_') + { + let log = root.join(format!("results/pending/{run_id}.log")); + if let Ok(text) = std::fs::read_to_string(&log) { + if text.contains("HOTPATH-PIPELINE-FAILED") { + let tail: String = text + .lines() + .rev() + .take(12) + .collect::>() + .into_iter() + .rev() + .collect::>() + .join("\n"); + return Ok(json!({ + "status": "failed", + "note": "the pipeline died before producing a verdict (usually a \ + candidate build failure); no ledger row was or will be \ + written for this run_id. Failure log tail follows.", + "log_tail": tail, + })); + } + } + } if std::time::Instant::now() >= deadline { return Ok(json!({"status": "running", "note": "no ledger row yet; the pipeline is still building/benching — poll again"}));