Today — reconverge's --message-format json prints one findings.v1 document per analyzed crate, one per line; its --help says so ("json prints one findings.v1 document per analyzed crate, one per line"). run_reconverge hands the whole of stdout to a single serde_json::from_str — crates/launchbound-prune/src/findings.rs:60-62, called at runner.rs:100 — so a second line is a parse error, and a parse error is a tool error for every candidate. Measured against 2.0.0 with the corpus reduce-flip kernel, unchanged except for a three-line src/main.rs:
$ cat src/main.rs
fn main() {
println!("host-side runner for the reduce kernel");
}
$ cargo check # the crate itself is fine
Finished `dev` profile
$ cargo reconverge check --strict --message-format json --cc 8.6 | wc -l
2 # one document for the lib, one for the bin
$ launchbound prune ./reduce-flip --cc 8.6 --reconverge-dir <reconverge 0.4.0 bin dir>; echo exit=$?
reduce-flip (cc 8.6):
! c1-9b9fe33407467e5e block_x=32 tile=128
TOOL ERROR (hard stop): findings.v1 parse failed: trailing characters at line 2 column 1
! c1-412245568e2c119a block_x=32 tile=256
TOOL ERROR (hard stop): findings.v1 parse failed: trailing characters at line 2 column 1
… the same line for all eleven …
=> 0 clean, 0 with caveats, 0 refused, 11 tool errors
exit=2
Control — the same kernel with src/main.rs deleted, same command: => 3 clean, 0 with caveats, 8 refused, 0 tool errors, exit 0. --json carries the same "verdict": "tool_error" for every candidate.
Why it is worth fixing — a src/main.rs beside a kernel library is the ordinary shape of a GPU crate: the host launcher lives there. Every such crate is refused by the gate wholesale, at exit 2, with a message that points at the analyzer's output ("findings.v1 parse failed") rather than at the reader — so the next stop is reconverge's tracker, where nothing is wrong. In the Action fail-on defaults to tool-error, so CI goes red for a crate that has nothing wrong with it.
The fail-safe itself holds: docs/SAFETY.md:26-27 makes unparseable output a hard stop, never a pass, and it stopped. It stopped against a format the analyzer documents. The one shape a JSONL reader has to handle — more than one line — is the one this reader cannot.
Fix — read stdout as JSONL: split on newlines, skip empty lines, parse each, require schema == "findings.v1" on each, and take the union of their findings — a deny finding in any target of the crate is a reason to refuse, and the bin target's document is harmless to merge. A line that fails to parse is still a tool error. Cheap, contained in run_reconverge, and it is the same change that makes a multi-crate kernel workspace possible later without touching the decision rule.
Separately: when parsing does fail, include the first two hundred bytes of what was received in the detail. "trailing characters at line 2 column 1" told this reproduction everything and would tell a user nothing.
Done when — a kernel crate with a lib and a bin target gets the same verdicts as the lib alone; launchbound-prune has a test that feeds a two-line stdout through the parse path; a genuine parse failure's tool-error detail shows what was received; the reconverge output contract is named in a comment at the parse site.
Today — reconverge's
--message-format jsonprints onefindings.v1document per analyzed crate, one per line; its--helpsays so ("json prints one findings.v1 document per analyzed crate, one per line").run_reconvergehands the whole of stdout to a singleserde_json::from_str—crates/launchbound-prune/src/findings.rs:60-62, called atrunner.rs:100— so a second line is a parse error, and a parse error is a tool error for every candidate. Measured against 2.0.0 with the corpusreduce-flipkernel, unchanged except for a three-linesrc/main.rs:Control — the same kernel with
src/main.rsdeleted, same command:=> 3 clean, 0 with caveats, 8 refused, 0 tool errors, exit 0.--jsoncarries the same"verdict": "tool_error"for every candidate.Why it is worth fixing — a
src/main.rsbeside a kernel library is the ordinary shape of a GPU crate: the host launcher lives there. Every such crate is refused by the gate wholesale, at exit 2, with a message that points at the analyzer's output ("findings.v1 parse failed") rather than at the reader — so the next stop is reconverge's tracker, where nothing is wrong. In the Actionfail-ondefaults totool-error, so CI goes red for a crate that has nothing wrong with it.The fail-safe itself holds:
docs/SAFETY.md:26-27makes unparseable output a hard stop, never a pass, and it stopped. It stopped against a format the analyzer documents. The one shape a JSONL reader has to handle — more than one line — is the one this reader cannot.Fix — read stdout as JSONL: split on newlines, skip empty lines, parse each, require
schema == "findings.v1"on each, and take the union of their findings — a deny finding in any target of the crate is a reason to refuse, and the bin target's document is harmless to merge. A line that fails to parse is still a tool error. Cheap, contained inrun_reconverge, and it is the same change that makes a multi-crate kernel workspace possible later without touching the decision rule.Separately: when parsing does fail, include the first two hundred bytes of what was received in the detail. "trailing characters at line 2 column 1" told this reproduction everything and would tell a user nothing.
Done when — a kernel crate with a lib and a bin target gets the same verdicts as the lib alone;
launchbound-prunehas a test that feeds a two-line stdout through the parse path; a genuine parse failure's tool-error detail shows what was received; the reconverge output contract is named in a comment at the parse site.