feat: 0.5.0 — read what the tool wrote - #113
Merged
Merged
Conversation
The text report copied the analyzed source straight to the terminal. A comment holding two real ESC bytes repainted the display, erasing every diagnostic printed above it while the summary went on counting them; a tab or a wide character before the span put the caret in the wrong cell; an 830-column line wrapped ten times on an 80-column terminal and scrolled its own header away. Source lines are now prepared before they are printed. C0, DEL and C1 bytes become Unicode control pictures, tabs expand to a fixed stop, the caret is measured in terminal cells rather than characters, and a line past 120 cells is trimmed around its span with an ellipsis marking the cut. The block is also indented by the width of its own line number, the way rustc does it, rather than pinned at the width that happened to suit a two-digit line. Spans themselves are untouched: findings.v1, SARIF and the baseline keep character columns, so every consumer keyed on them is unaffected. Stdout writes go through a locked handle and a shared classifier, so `check | head` no longer panics with exit 101 on the broken pipe -- the verdict is already computed, so the run keeps its exit code. The properties live on a real PTY, via the termlens skill: an escape that erases and a caret in the wrong cell are both properties of the rendered grid, which a stdout string cannot see. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
Four gaps that share a shape: a check that could not see the thing it was meant to check. `conformance/extractor` declares its own `[workspace]`, and every gate in the repository is workspace-scoped, so the crate that decides what the published precision table measures had no lint, no format check and no doc build. Its clippy was already red on a dead helper. fmt and clippy now run against its manifest too, in CI and in `just ci`, and `call_name` -- genuinely unreachable, the path handling lives in its callers -- is gone. Both gate scripts prune a member with GNU `sed -i`. BSD sed, which macOS ships, takes a mandatory operand after `-i`, so the expression was eaten as a backup suffix and the run died on `invalid command code C`. Neither prune is conditional, so no macOS contributor could run either gate as committed. A `sed_inplace` helper in both, verified byte-identical to the old form, and the macOS job now at least parses the scripts. `notify-testing-repo` was guarded on `github.event_name == 'push'` in a workflow with no `push:` trigger, so it had never run once -- skipped, not failed, beside three green jobs. It now keys on `!inputs.dry_run`, takes its version from `inputs.tag` rather than a branch name, and names a non-2xx from the dispatch API instead of swallowing it. And the counts: the published table still read `466 gating finding(s)`, pinned there byte-for-byte by the mutation gate's own diff, because the extractor could not reach `reconverge_artifacts::plural` across the workspace boundary. It can now. scripts/check-plurals.sh is the gate that was missing -- a count followed by a parenthesized plural, in Rust, shell, markdown or YAML -- and it flags a count, not deliberately generic prose, so `the element(s) its own index selects` stays as written. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
Eight findings that all reduce to reading something the tool wrote and never questioned. **The schema tag.** Every artifact carries a `schema` field whose only purpose is to be checked, and nothing checked it. A document declaring itself `findings.v1`, from a tool that is not this one, suppressed a deny-tier finding as happily as a real baseline; a `findings.v99` written by a driver this build has never met was merged, rendered, gated on and re-published on stdout while the SARIF of the same invocation stamped a different producer. `reconverge_artifacts::read::deserialize_checked` is the one helper all four readers now go through, so a fifth cannot reintroduce the gap and every surface refuses in the same words. **The build scope.** `check` built with a bare `cargo check`, so cargo's own package selection applied while the report and the exit code came from every member `cargo metadata` lists. Run the gate from inside a member directory, or add a `default-members` line, and the sibling was never re-linted -- its previous, clean artifact was printed as this run's answer, with the right paths, in the right order, exit 0. It is `--workspace` now, and a member that still produces nothing is named and exits 2 rather than passing by omission: the self-heal already computed that set, forced a rebuild with it and then discarded it unread. **The driver's identity.** `cargo install reconverge-driver` over the same path re-linted nothing -- cargo does not see a same-path wrapper whose contents changed -- so CI kept gating on whatever the old driver concluded. The driver's path, size and mtime now live in the `cc-marker` beside `--cc`, and `reconverge-driver --reconverge-version` finally answers which build did the analysis, while every other argv still reaches rustc untouched. **One document per target.** A lib and a bin compile under one crate name and wrote two `findings.v1` documents with nothing to tell them apart -- and the driver's own comment told consumers to key on `crate`. `target` is additive within v1, and the sort key is total, so two projects of identical shape stop emitting their documents in different orders. **Coverage as a property of the run.** It was a note on RC001 and nothing else, so it was missing from four codes and from the case where it is load-bearing: a kernel whose divergent barrier is spelled in `asm!` has no finding to hang a note on, and `--strict` exited 0 over it. `findings.v1` carries the tally structurally now, the summary line declares it, and the note reaches all five codes. **SARIF.** The provenance walk is `relatedLocations` and `codeFlows`, the rules carry a `helpUri` to their explain page, and a rule's default level is a property of the code rather than of whichever result came first. **The collective's lane strip.** Its departures were recorded one step after the call, so the strip read all 32 lanes active two rows above an `active 0x55555555` saying sixteen -- in the one view a user opens because they do not yet believe the finding. The deltas are at the site now, and the invariant is written down and asserted, on driver artifacts as well as fixtures. **The fixtures.** All three witness documents were hand-written, showed MIR statements no released driver has ever emitted, and were stamped `tool.version 0.0.0`. `scripts/record-fixtures.sh` records them from a real `check` and CI diffs them, so the API tests test the producer. That turned the collective golden red immediately, which is the proof the fixtures had been describing something else. `witness.v1` also stops claiming 32 lanes: the declared-block replay has written 64, 96 and 128 since 0.1.12, and the artifacts breaking the published bound were exactly the gating ones. lint-samples now emits one, plus the unmasked wrapper and the `asm!` kernel that had no coverage anywhere. Also: a baseline is written via a temp file and renamed, so a failed write can no longer truncate the one checked-in record of what a human reviewed. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
…e write The same half-written file got three different diagnoses depending on which mode it was handed to, and one of those modes then destroyed a reviewed baseline while reporting success. The shell view already sniffed correctly; the three mode loaders each rolled their own five lines with `.ok()` and `.unwrap_or_default()`, which threw away the parse position and printed `unsupported schema ``` -- an empty pair of backticks naming nothing at all. That message is a *version* statement: it sends the reader to reinstall the driver and go read `schemas/`, none of which helps a file that is simply truncated. And a truncated file is easy to get: the driver writes artifacts with no atomic rename, so a Ctrl-C during `check` leaves one behind. `sniff_schema` is now one function with three callers, and a well-formed document with no `schema` key reports `(missing)`, which is a different thing. inspect was worse in a different way: its `errors` field was written at four sites and read at none, so a truncated findings file, a nonexistent path and a valid file with zero findings all rendered as a working inspector with nothing to show -- the footer that means "you passed no findings file" -- with the same exit code as a successful run. triage rendered its errors only inside the `items.is_empty()` branch: the one case where it has nothing to review and nothing to write. So a baseline that failed to parse opened looking entirely normal, 23 findings listed, `0 suppressed`, and `w` -- which has no dirty guard -- replaced every reviewed acceptance and its ticket number with an empty document and said `baseline written`. That is the loop a maintainer walks *because* `check` just complained about that file. The errors now render above the list, `w` is refused while the baseline is unreadable, and `cargo reconverge triage --baseline <broken>` exits 2 before the TUI opens with the same message `check` prints, so the same flag on the same path means the same thing in both subcommands. Tests: the five damaged shapes per view, four corruption shapes driven through a full review pass with the file's bytes compared before and after, and a golden of the error line beside a non-empty list so it cannot be gated back inside the empty branch. Every loader test in this crate previously handed the loader a well-formed document with a wrong `schema` string -- the one damaged shape that already worked -- and `inspect/data.rs` had no loader test at all. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
…s claim The integration tests these findings each named as "the job that would have caught it", plus the prose that had drifted from the binary. Tests: a two-member workspace run from the root, from a member directory, and with a `default-members` line -- nothing in this repository had ever run `check` over a workspace with two members, which is why the build and the report could disagree about what "the workspace" means. A lib+bin package asserting two documents that name their targets, in a total order. `check` into a reader that closes early, in text and JSON. Every subcommand answering `--help`. A kernel fixed between two runs, asserting its witness is gone. A doctored artifact refused by its schema tag and a version skew named on stderr. The driver binary swapped in place between two runs, asserting the second re-lints -- counting cargo's `Checking` line, not the driver's, because cargo replays cached compiler output and that replay is exactly what made the bug look like a re-analysis. And `--reconverge-version`, beside proof that `-vV` and `--version` still reach rustc untouched. Docs: the README's witness frame is re-shot from the recorded fixture, so it shows what a reader gets from their own kernel rather than five MIR steps no driver emits. The multi-warp bullet stated its own limitation twice, oppositely, four lines apart -- the superseded sentence is deleted rather than negated in place, and ARCHITECTURE stops calling the interpreter 32-lane. The opaque-regions promise now describes what the tool prints. `--explain RC002` no longer calls the unmasked wrappers unchecked while they are the confirmed finding that just failed the reader's CI, no longer says an exactly-matching mask is unflagged, and its snippet uses `warp::lane_id`, which exists. `simt.rs` stops citing that page as its authority for a gap that closed in #21. The crates.io install page offered an unpinned `cargo install` as "the manual equivalent" of a command that pins -- on the page where the install actually happens. Both READMEs now carry the `@VERSION` pins and the clause saying why, and a unit test compares each against `setup`'s own plan. Two derived checks rather than restated claims: an explain page may not call a construct the dialect classifies "out of scope", and scripts/check-schemas.sh validates `fixtures/` and what an end-to-end `check` emits against `schemas/`. The second is confirmed red against the `const: 32` bound v0.4.0 published. Also: `witness` filters on the findings this run still has, so an artifact left by an older reconverge is not replayed as current either. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
Bump the workspace and both lockfiles, re-record the fixtures at the new version, and write the changelog: 27 findings, all reported against 0.4.0 with a measured reproduction. The gate documents gain what each finding asked for. CONTRIBUTING names the standalone-manifest rule -- a new crate that declares its own `[workspace]` is invisible to every gate here by construction, which is how `conformance/extractor` sat outside all of them with a red clippy while `required-green` reported success. RELEASING gains the step that would have caught `notify-testing-repo`: read the finished run's job list and explain every job that is not green, because a skipped job beside three green ones looks exactly like a job that worked. AGENTS records that the witness fixtures are recorded rather than written, that round-tripping through serde is not validating, and where the termlens skill lives. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
`CARGO_TERM_COLOR: always` is set for every CI job, so cargo's progress line arrives as `\e[1m\e[92m Checking\e[0m lint-samples v0.0.0 (…)` and the literal `"Checking lint-samples"` is not a substring of it. The two re-lint assertions therefore counted zero in CI whatever happened: green locally, and a tautology on the runner -- which is exactly the shape of test this release exists to stop shipping. The words are matched separately now, verified against a real colored stderr rather than assumed. Signed-off-by: Vyncint Ng <115854244+vyncint@users.noreply.github.com>
This was referenced Sep 5, 2026
Closed
Closed
Closed
Closed
Closed
This was referenced Sep 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #68, #70, #71, #74, #82, #83, #88, #89, #90, #91, #92, #93, #94, #95, #96, #97, #99, #101, #102, #103, #104, #105, #106, #107, #108, #109, #110.
All 27 open findings, each verified against the reproduction it carries, plus the 0.5.0 release.
The theme
Nine of these reduce to one shape: an artifact, a document or a page was produced correctly and then consumed without ever being questioned.
"schema": "findings.v1", from a tool that is not this one, suppressed a deny-tier finding.cargo metadatalists, so a deny-tier RC003 and a confirmed RC001 in an unbuilt sibling printed as0 deny, 0 confirmed, exit 0.What is verified rather than asserted
Every fix lands with a test that can fail, and three of them were watched going red first:
scripts/check-schemas.shis red against theconst: 32bound v0.4.0 published — the artifacts that violated it were exactly the gating ones.scripts/check-plurals.shis red against a probefinding(s)string.Notable scope calls
witness.v1is widened in place, not versioned. The schema has been unsatisfiable for the multi-warp case since 0.1.12, and every artifact that validates today still validates after, so this records reality rather than repurposing a field.findings.v1gainstargetandcoverageadditively, with#[serde(default)], so every existing fixture still parses. Documents are now one per target, and all four places that said "per crate" say so.checkand CI diffs them.reconverged-clean.jsonis the one that cannot be — a witness is written only for a confirmed finding, and a kernel that reconverges has none — so it stays hand-written, in the driver's own voice, andfixtures/README.mdsays so.jsonschemadependency: a new dependency is a stop-and-ask event under CONTRIBUTING §0.4. A keyword it does not implement is a hard error, never a silent pass.#95's strongest option was not taken. Promoting each explain page's kernels intolint-samplesand asserting each page's stated verdict against a real run costs a fixture kernel per page. The derived check that shipped — a page may not call a construct the dialect classifies "out of scope" — pins exactly the class that drifted here and says nothing about verdicts.#88's deeper option was not taken.wover an unreadable baseline is refused rather than redirected to<path>.new; the refusal is the cheap move that keeps the redirect possible later.What this leaves out
TESTING_REPO_DISPATCH_TOKENis still unset, sonotify-testing-repowill run, print its notice and dispatch nothing. That is the point of fixing the guard first: the notice is the first honest signal this wire has ever produced.#90also asks foractionlintover.github/workflows/**. Not added — it is a new CI dependency and a separate decision.Numbers
conformance/MUTATION.mdregenerates byte-identically.cargo test --workspacegreen,just cigreen,cargo deny checkclean, both gate scripts green.