Skip to content

feat: 0.5.0 — read what the tool wrote - #113

Merged
vyncint merged 7 commits into
mainfrom
feat/v0.5.0
Sep 5, 2026
Merged

feat: 0.5.0 — read what the tool wrote#113
vyncint merged 7 commits into
mainfrom
feat/v0.5.0

Conversation

@vyncint

@vyncint vyncint commented Sep 5, 2026

Copy link
Copy Markdown
Owner

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.

  • The gate read a findings artifact, rendered it, gated on it and re-published it on stdout without asking whether it was one of its own — the only validation was the filename, under an error message that already promised the check.
  • The baseline loader never read the field whose only purpose is to be read, so a document saying "schema": "findings.v1", from a tool that is not this one, suppressed a deny-tier finding.
  • The build compiled whatever cargo picked from the cwd while the report covered every member cargo metadata lists, so a deny-tier RC003 and a confirmed RC001 in an unbuilt sibling printed as 0 deny, 0 confirmed, exit 0.
  • The witness debugger replayed a finding that no longer existed, and its lane strip contradicted the mask row two lines beneath it.
  • A baseline that failed to parse opened looking entirely normal and one keystroke replaced every reviewed acceptance with an empty document, reporting success.

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.sh is red against the const: 32 bound v0.4.0 published — the artifacts that violated it were exactly the gating ones.
  • scripts/check-plurals.sh is red against a probe finding(s) string.
  • Re-recording the witness fixtures from a real run turned the collective golden red immediately, which is the proof the fixtures had been describing something else.

Notable scope calls

  • witness.v1 is 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.v1 gains target and coverage additively, 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.
  • The witness fixtures are recorded from a real check and CI diffs them. reconverged-clean.json is 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, and fixtures/README.md says so.
  • The schema validator is hand-rolled, a Draft 2020-12 subset, rather than a jsonschema dependency: 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 into lint-samples and 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. w over 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_TOKEN is still unset, so notify-testing-repo will 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.
  • #90 also asks for actionlint over .github/workflows/**. Not added — it is a new CI dependency and a separate decision.
  • The pinned nightly and the cuda-oxide rev are unchanged.

Numbers

  • Conformance: 0 false positives at default confidence; gating findings match the reviewed baseline exactly.
  • Mutation corpus: precision 1.000 over 466 gating findings; conformance/MUTATION.md regenerates byte-identically.
  • Suite: cargo test --workspace green, just ci green, cargo deny check clean, both gate scripts green.

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>
@vyncint
vyncint merged commit a147f45 into main Sep 5, 2026
11 checks passed
@vyncint
vyncint deleted the feat/v0.5.0 branch September 5, 2026 13:49
This was referenced Sep 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The baseline loader never checks the schema tag, so any document with entries can suppress a deny finding

1 participant