From 26527e7dae6d7a6844594acd8bf485e28999fd29 Mon Sep 17 00:00:00 2001 From: Timmy Date: Fri, 15 May 2026 23:13:38 +0100 Subject: [PATCH] diag(1101): log classify verdict + matched trigger on merge gate failures Bug 1101's reframed AC1: when a non-success merge runs, log the typed GateFailureKind, the matched classifier-trigger substring (if any) and ~90 chars of surrounding context. Fires on every gate failure regardless of routing, so the next fixup-loop bounce will tell us which substring is fooling classify() into Fmt|Lint|SourceMapCheck on what's actually a Test failure. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/agents/pool/pipeline/merge/runner.rs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/server/src/agents/pool/pipeline/merge/runner.rs b/server/src/agents/pool/pipeline/merge/runner.rs index f04026a2..a950cb68 100644 --- a/server/src/agents/pool/pipeline/merge/runner.rs +++ b/server/src/agents/pool/pipeline/merge/runner.rs @@ -186,6 +186,50 @@ impl AgentPool { .map(|k| k.is_self_evident_fix()) .unwrap_or(false); + // Bug 1101 diagnostic: log the classified failure_kind and the + // matched classifier-trigger substring with surrounding context, + // so we can confirm whether classify() is incorrectly matching + // a passing-step stdout substring (e.g. "Diff in " inside a + // failing test's panic message) and bouncing the story to a + // fixup coder. Remove once the fix lands. + if let Ok(r) = report.as_ref() + && let crate::agents::merge::MergeResult::GateFailure { + output: gate_output, + failure_kind: Some(k), + } = &r.result + { + const TRIGGERS: &[&str] = &[ + "CONFLICT (content):", + "Merge conflict:", + "Diff in ", + "would reformat", + "missing-docs direction", + "error[clippy::", + "warning[clippy::", + "missing_doc_comments", + "error[E", + ]; + let matched = TRIGGERS + .iter() + .find_map(|t| gate_output.find(t).map(|i| (*t, i))); + let (trigger, context) = match matched { + Some((t, i)) => { + let start = i.saturating_sub(30); + let end = (i + t.len() + 60).min(gate_output.len()); + let ctx = gate_output + .get(start..end) + .unwrap_or("") + .replace('\n', " "); + (Some(t), ctx) + } + None => (None, String::from("")), + }; + slog!( + "[merge] classify diagnostic for '{sid}': failure_kind={k:?} \ + is_fixup={is_fixup} trigger={trigger:?} context='{context}'" + ); + } + if is_no_commits { let reason = kind.display_reason(); if let Err(e) = crate::agents::lifecycle::transition_to_blocked(&sid, &reason) {