What happens
A decision in .procoder/ask/decisions.md whose body quotes gate output (for example as an indented code block) is read back by the commit hook as a live finding. In azrtydxb/novamem a decision section contained:
Every commit in this repo currently fails:
BLOCKING toml 4.1.1 has 2 known vulnerability(s), max severity 8.2 — upgrade it (security)
From then on every commit that staged decisions.md was blocked with BLOCKING toml 4.1.1 has 2 known vulnerability(s)…. procoder security reported 0 blocking and no manifest in the repo contained toml 4.1.1. It looked intermittent because it appeared only when decisions.md was staged (the ask collector runs in the change-scoped hygiene pass).
Repro (procoder 3.7.0)
.procoder/config.toml with [ask] / policy = "block" (or any other blocking finding, so the gate exits non-zero).
.procoder/ask/decisions.md with a ## heading whose body contains BLOCKING toml 4.1.1 has 2 known vulnerability(s) ... (security).
- Stage it and feed
{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git commit -m x"},"cwd":"<repo>"} to procoder hook pre-tool-use, or run procoder check .procoder/ask/decisions.md.
The deny reason lists three blocking findings; the third is BLOCKING toml 4.1.1 has 2 known vulnerability(s)…, which no check produced.
Cause
internal/ask/gate.go GateFindings builds each finding's Message as q.Text + " (ask)". For a decision, q.Text is the heading plus the whole multi-line body (decisionQuestions). The gate prints a finding as one mark file message line, so the message's embedded newlines become extra output lines. internal/hook/commit.go blockingLines then scans the gate output line by line and keeps any line whose trimmed text starts with BLOCKING , UNCHECKED or unformatted . A quoted body line starting with one of those prefixes is indistinguishable from a real finding.
Impact
A recorded decision can block every commit that touches it with a phantom finding no tool can reproduce or clear; the only way out is hand-editing the decision text. The same applies to UNCHECKED and unformatted lines, and to any consumer that parses gate output line by line.
Expected
Text from a decision body can never be parsed as a gate finding: a finding is exactly one output line.
What happens
A decision in
.procoder/ask/decisions.mdwhose body quotes gate output (for example as an indented code block) is read back by the commit hook as a live finding. In azrtydxb/novamem a decision section contained:From then on every commit that staged
decisions.mdwas blocked withBLOCKING toml 4.1.1 has 2 known vulnerability(s)….procoder securityreported 0 blocking and no manifest in the repo contained toml 4.1.1. It looked intermittent because it appeared only whendecisions.mdwas staged (the ask collector runs in the change-scoped hygiene pass).Repro (procoder 3.7.0)
.procoder/config.tomlwith[ask]/policy = "block"(or any other blocking finding, so the gate exits non-zero)..procoder/ask/decisions.mdwith a##heading whose body containsBLOCKING toml 4.1.1 has 2 known vulnerability(s) ... (security).{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git commit -m x"},"cwd":"<repo>"}toprocoder hook pre-tool-use, or runprocoder check .procoder/ask/decisions.md.The deny reason lists three blocking findings; the third is
BLOCKING toml 4.1.1 has 2 known vulnerability(s)…, which no check produced.Cause
internal/ask/gate.goGateFindingsbuilds each finding'sMessageasq.Text + " (ask)". For a decision,q.Textis the heading plus the whole multi-line body (decisionQuestions). The gate prints a finding as onemark file messageline, so the message's embedded newlines become extra output lines.internal/hook/commit.goblockingLinesthen scans the gate output line by line and keeps any line whose trimmed text starts withBLOCKING,UNCHECKEDorunformatted. A quoted body line starting with one of those prefixes is indistinguishable from a real finding.Impact
A recorded decision can block every commit that touches it with a phantom finding no tool can reproduce or clear; the only way out is hand-editing the decision text. The same applies to
UNCHECKEDandunformattedlines, and to any consumer that parses gate output line by line.Expected
Text from a decision body can never be parsed as a gate finding: a finding is exactly one output line.