File/Line
internal/agent/verify_suppress.go L128-154 (threshold logic), wired at internal/agent/agent.go L3575
Problem
The comment contract states: "Any suppression on non-verification commands = warn after 2 occurrences". The implementation only honors it for error-masking:
} else {
maskCount := 0
for _, c := range s.suppressedCmds {
if c.category == "error-masking" { // output-hiding never counted
maskCount++
}
}
if maskCount >= 2 { shouldFire = true }
}
The verification-command branch correctly counts ALL categories (len(s.suppressedCmds) >= 2); the non-verification branch silently drops output-hiding occurrences.
Trigger scenario (false negative)
cmd1: echo step1 2>/dev/null → suppressedCmds=[output-hiding], maskCount=0
cmd2: ls -la 2>/dev/null → ×2, maskCount=0
cmd3: cat f.txt 2>/dev/null → ×3, maskCount=0 — never fires
An agent can systematically hide stderr on every non-verification command (e.g. silencing build noise) and the reward-hacking detector never injects its warning — the exact pattern the file's research basis ("npm test 2>/dev/null", ICML 2026 reward hacking) calls out.
Expected vs actual
- Expected: any suppression category ×2 on non-verification commands fires the warning
- Actual: only error-masking counts; output-hiding accumulates unboundedly with zero feedback
Fix
Count both categories in the non-verification branch (maskCount → count of all suppressedCmds, or explicitly both categories), matching the comment and the verification-branch semantics.
Severity
Medium — exit codes still surface (error-masking remains caught), but a documented suppression exploit escapes detection; no test asserts the current gap (TestCheckVerificationSuppression_NonVerifyNeedsTwoMasks only covers error-masking).
Verified by independent review subagent (sa-9, static trace of both branches + test coverage check).
File/Line
internal/agent/verify_suppress.goL128-154 (threshold logic), wired atinternal/agent/agent.goL3575Problem
The comment contract states: "Any suppression on non-verification commands = warn after 2 occurrences". The implementation only honors it for error-masking:
The verification-command branch correctly counts ALL categories (
len(s.suppressedCmds) >= 2); the non-verification branch silently drops output-hiding occurrences.Trigger scenario (false negative)
An agent can systematically hide stderr on every non-verification command (e.g. silencing build noise) and the reward-hacking detector never injects its warning — the exact pattern the file's research basis ("npm test 2>/dev/null", ICML 2026 reward hacking) calls out.
Expected vs actual
Fix
Count both categories in the non-verification branch (
maskCount→ count of allsuppressedCmds, or explicitly both categories), matching the comment and the verification-branch semantics.Severity
Medium — exit codes still surface (error-masking remains caught), but a documented suppression exploit escapes detection; no test asserts the current gap (
TestCheckVerificationSuppression_NonVerifyNeedsTwoMasksonly covers error-masking).Verified by independent review subagent (sa-9, static trace of both branches + test coverage check).