From b11f5f8d2cfbe5e9ed3ca14713a7eef8d7882a70 Mon Sep 17 00:00:00 2001 From: metaphorics <152830360+metaphorics@users.noreply.github.com> Date: Thu, 11 Jun 2026 04:51:18 +0900 Subject: [PATCH] fix(pi-shell): strip golangci info/warn logs but keep level=error is_golangci_noise drops 'level=info'/'level=warn' runner chatter while preserving 'level=error' lines, which carry config and typecheck failures that must reach the agent. Op: correct Restores: spec:snip/filters/golangci-lint.yaml --- crates/pi-shell/src/minimizer/filters/go.rs | 45 ++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/crates/pi-shell/src/minimizer/filters/go.rs b/crates/pi-shell/src/minimizer/filters/go.rs index b45cabcc5..9d7275420 100644 --- a/crates/pi-shell/src/minimizer/filters/go.rs +++ b/crates/pi-shell/src/minimizer/filters/go.rs @@ -373,7 +373,19 @@ fn is_go_noise(line: &str) -> bool { fn is_golangci_noise(line: &str) -> bool { let lower = line.to_ascii_lowercase(); - lower.starts_with("level=") && lower.contains("msg=\"[linters_context]") + // Strip runner-log info/warn chatter (snip strips `^level=` wholesale), but + // DELIBERATELY KEEP `level=error` — those lines carry config/typecheck failures + // that would otherwise vanish silently. + // `level=error` carries config/typecheck failures (incl. the canonical + // `level=error msg="[linters_context]…"` typecheck headline) — never strip it, + // even when it routes through the linters_context component. + if lower.starts_with("level=error") { + return false; + } + lower.starts_with("level=info") + || lower.starts_with("level=warning") + || lower.starts_with("level=warn") + || lower.starts_with("level=") && lower.contains("msg=\"[linters_context]") || lower.starts_with("golangci-lint has version") || lower.starts_with("running ") && lower.contains("linters") } @@ -551,6 +563,37 @@ mod tests { ); } + #[test] + fn golangci_strips_info_warn_but_keeps_level_error() { + let cfg = MinimizerConfig { enabled: true, ..Default::default() }; + let ctx = MinimizerCtx { + program: "golangci-lint", + subcommand: Some("run"), + command: "golangci-lint run ./...", + config: &cfg, + }; + // Real default (non-json) golangci run: the typecheck headline is emitted by + // the logrus logger in its canonical `level=error msg="[linters_context]…"` + // shape — the exact format that must survive. Surrounding runner chatter + // (incl. a warn-level linters_context line) is stripped. + let input = "level=info Active 5 linters\nlevel=warning The linter 'deadcode' is \ + deprecated\nlevel=warning msg=\"[linters_context] stale cache\"\nlevel=error \ + msg=\"[linters_context] typechecking error: cannot find package\"\nmain.go:10:2: \ + undefined: Foo (typecheck)\n"; + let out = filter(&ctx, input, 1); + assert!(out.text.contains("level=error")); + assert!(out.text.contains("typechecking error")); + // `group_by_file` regroups the per-issue line under a `main.go:` header, + // so assert on the surviving location + linter, not the joined string. + assert!(out.text.contains("main.go")); + assert!(out.text.contains("undefined: Foo (typecheck)")); + assert!(!out.text.contains("level=info")); + assert!(!out.text.contains("level=warning")); + // The warn-level linters_context chatter is still dropped — the keep is + // scoped to error level, not to every linters_context line. + assert!(!out.text.contains("stale cache")); + } + #[test] fn unknown_go_tool_is_passthrough() { let cfg = MinimizerConfig { enabled: true, ..Default::default() };