v2.1.0 — read what the tool was handed
Thirteen findings, all reported against 2.0.0 with a measured reproduction.
Most of them reduce to one shape: something was consumed on a contract other
than the one it is written to, and the mismatch was reported as a fault of
whatever it was pointed at.
Changed
-
The lockstep pins move to reconverge 0.5.0, at every recorded site.
2.0.0 moved the gate to 0.4.0 and updated four of the six the policy
names, sorust-toolchain.toml,CONTRIBUTING.mdandpins.ymlstill
recorded 0.1.11 — andpins.ymlmeasures upstream drift against its own
RECONVERGE_PIN, so its weekly signal reported movement away from a
version nothing installs. That is why #17 sat open describing a pin two
releases old.just pinsnow asserts the sites agree with each other,
with no network, before anything asks upstream. Reported in #46 and #17. -
apply --verifyis a real switch. It was aboolwith
default_value_t = true, which clap gives aSetTrueaction — so
--verifyset what was already set,--no-verifywas an unknown
argument, and the help read as an opt-in for something mandatory. There
is a--no-verifynow, and the help says verification is on. This is
what makesapplyusable on a machine that has the run directory but not
the analyzer and the pinned toolchain. Reported in #36. -
prune's verdict line says what it checked. The gate answers
convergence and static shared-memory capacity at a--cc; it has no view
of instruction availability, so a crate using ansm_80+intrinsic under
needs_cc = "7.5"prunes to12 cleanat--cc 7.5and fails only when
something finally lowers it for that part.needs_ccis the author's
claim and is taken on trust — defensible, and nowhere stated, so "clean"
read as "this kernel is fine at cc 7.5". docs/LIMITATIONS.md carries the
long form, including why the two stronger fixes were not built. Reported
in #32.
Fixed
-
A kernel crate with a bin target no longer hard-stops the gate.
reconverge prints onefindings.v1document per analyzed target — its
documented contract — and this reader handed the whole of stdout to a
singlefrom_str, so asrc/main.rsbeside a kernel library, the
ordinary shape of a GPU crate, made every candidate a tool error at
trailing characters at line 2 column 1. In the Actionfail-on
defaults totool-error, so CI went red for a crate with nothing wrong
with it, pointing at the analyzer's tracker. Stdout is read as JSONL and
the findings are unioned: a deny finding in any target refuses, and the
bin target's document is harmless to merge. The fail-safe always held —
it held against a format the analyzer documents. Reported in #42. -
The scratch copy is the whole crate. It took only the entries of
src/that are files, somod util;withsrc/util/mod.rs— how Rust
code is organised past one file — produced a scratch crate that could not
compile, and the gate reportederror: could not compileagainst a crate
whose owncargo checkis clean. The message told its author to fix
build errors they do not have, or to reinstall their toolchain, and never
said that what it compiled was not their crate. It copies recursively
now, carriesbuild.rsor whateverpackage.buildnames, skips
target/, and a tool error names the scratch directory. Reported in #43. -
rustc's diagnostics survive the tool-error filter. #19 replaced a
tail-six heuristic with a filter on marked lines, and picked the
secondary marker: rustc's primary diagnostics beginerror[E0583]:,
a code before the colon. So what survived was cargo's summary and
reconverge's generic hint — "see the errors above", with the one that was
above removed. Both forms are accepted now, here and in the compile
executor, which still had the tail heuristic #19 removed next door.
Reported in #44. -
--budgetcannot be given a value that fails to bound anything.
split_at(text.len() - 1)on a trimmed-empty argument is0usize - 1,
so--budget ""panicked at exit 101. Worse:NaNand1e400parsed,
and the guard iselapsed >= budget— false for every value against NaN,
never true against infinity — so a value that looked accepted produced an
unbounded measured sweep on real silicon, which is the one failure a
budget exists to prevent. Non-finite and negative values are rejected,
the message names the flag and the accepted forms, andmin/hrare
accepted alongsidem/hbecause that is what people type.--budget 0
stays valid and means what it always did. Reported in #33. -
applydecides about verification before it prints anything. It
emitted theparams.rsand then verified, so "refusing to emit"
arrived after the emission and a reader who had piped stdout to a file
had the file. On a Metal run it could never succeed at all: that path has
no convergence gate, deliberately, so the run recordsgate_cc: "metal",
and handing that sentinel to reconverge produced the correct answer to
the wrong question ("metalis not a compute capability") dressed as a
regression ("no longer passes the gate"). Nothing regressed; the gate
never ran and cannot. It refuses by name now, before stdout, and points
at--no-verifyorprune --cc <target>.Verdicthas aDisplay, so
no user-facing message is a Rust struct literal. Reported in #34. -
A
results.jsonthe report cannot read is an error, not "unmeasured".
A truncated file, an empty one,null,[], aresults.v2from a newer
runner and a directory namedresults.jsonall rendered as "nothing
measured yet": exit 0, nothing on stderr, and a JSON report that
validated against the schema. The run directory is the hand-off between
two machines and those two conditions call for opposite actions — wait,
or go and look. OnlyNotFoundisOk(None)now; everything else names
the path and the cause, the wayverdicts.v1already did fifteen lines
away in the same function.model --resultsnames the cause too, and
tune's end-of-run report inherits all of it. Reported in #45. -
Nothing is cut mid-value or mid-word at the panel border. #24 fixed
this on the chosen line and left it in the two views below: the ranking
lost every closing bracket at eighty columns, so each interval read as a
number with no upper bound, and the rejections view — the one the README
calls the point of the tool — lost the clause that says what to do,
stopping atsplits a 64-threaand never reachingsafe only at one warp (<= 32 threads). An interval is a field and is dropped whole through the
same helper the chosen line uses; a reason is a sentence and wraps. A
scan over every golden now fails on a value or a word ending at the
border without an ellipsis — confirmed red against the shipped pre-fix
frames, and it would have caught #24. Reported in #35. -
A missing
cargo oxideis diagnosed by name. cargo's own help
relayedcargo search cargo-oxide, and cargo-oxide is not on crates.io:
cuda-oxide is a pinned git checkout. The one actionable-looking line sent
the reader to a package that does not exist, on the first wall ofstage
andtune --backend cuda. The message now names the pin, gives the three
commands CI uses, and says thatpruneneeds none of it. The pin is a
constantjust pinschecks, so a bump moves the message with it. In the
same path,exit Some(101)is an exit code again and the compile failure
reports the compiler's errors rather than its summary. Reported in #37. -
No flag on
tuneis accepted and silently ignored.--budget,
--orderand--seedare inert with--backend model, and--seedis
inert with--order exhaustiveon any backend. Each says so once, the
way--outhas since #22 — somebody who passes--budget 30mreasonably
believes something is bounded. Reported in #38.
Install
$ cargo install launchbound-cliIn CI, the action is three lines and follows 2.x on its own:
- uses: actions/checkout@v7
- uses: vyncint/launchbound/action@v2
with:
cc: "8.6"The gate installs cargo-reconverge 0.5.0 from crates.io on every run — a
cached analyzer is not evidence about what is published today.