certify: the oracle never panics, and the release legs after #507 - #534
Merged
Conversation
THE ORACLE PANICKED, so it was not an oracle. An array, map or keyed body is framed by its own `L`, and the three readers compute `end := r.off + bodyLen` and later span the elements with `r.sub(end - r.off)`. Between those two lines they read the body's header, whose count is a canonical LEB128 read against the PARENT buffer rather than against `end`. A body's `L` of 2 is the shortest that carries a header at all, but a LEB128 count is up to ten bytes, so a count spelled wide leaves the cursor past the end its own `L` set and the span goes negative. The C++ reference has the same shape at every one of its sites, but a negative span there is a reader whose `has()` refuses, so it decodes no elements and reports malformed on the first one asked for. `subTo(end)` gives the oracle that same answer by clamping the span at the cursor, and all three body readers go through it, so the invariant is stated once. The reference agrees on the mutant, replayed alone and across the whole run: 0 divergences. THE MUTANT IS PINNED. testdata/wire/tables/fuzz-vectors/ is a corpus of mutants the fuzzer has already gone red on, seeded into every run by name and fed exactly as it is, before the random pass. Vectors are never mutated and the random pass never draws from them, so the mutant sequence stays a function of the corpus and the seed alone. A RED IS A SEEK. The run seed was already deterministic, but the failure message printed neither it nor the mutant. It prints both now, the bytes as hex up to 4 KB and the SHA-256 above that. The negative control is tables-wire-fuzz-oracle-negative-control, which joins tables-wire-fuzz-negative-control and so runs in `make test`: it removes the clamp through a Go build overlay and requires the run to go red on the panic AND on the pinned vector by name. THE PORT GATES THAT READ THE ID-TABLE CORPUS. Four release legs failed on one cause: they hold a port's codec to testdata/wire/tables, which is the id-table form now, while the port still writes the wire's previous form. #507 already ruled on that shape and spelled it as dormancy; what it missed is every gate that calls a port's wire codec directly rather than through the conformance harness. The same rule now covers those, by name, citing the same issue per port: #511 Go, #512 C, #513 C#, #514 Dart, #515 Elixir, #516 JavaScript, #517 Java. No Rust gate reads the corpus. It reaches past the release legs: `make test` runs the port legs after the wire fuzzer, so the panic masked them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…d id The gate is the next red on main behind the wire fuzzer's panic, and it is #507's too. The id-table form's reserved node-table id is `kTableNodeTableFieldId` in every generated unit, because every reader of the form owes §3.1's refusal of that id inside a NESTED body whether or not its own closure carries a pointer: the body it is handed may have been written by a unit that does (§4). The gate's `TableNode` token matched the constant and called it pointer machinery. What a pointer-free unit pays for the rule is one `static const uint64_t` and one comparison. No arena, no builder, no handle, no lifecycle surface and no extra descriptor column, which is the claim this gate holds. So the scan is BY SYMBOL now rather than by line, `TableNode` is matched with its whole spelling so a node symbol nobody has written yet is still refused, and exactly one spelling is sanctioned by name. tables-zero-cost-negative-control plants TableNodeMap in a copy of a scanned header and requires the same scan to refuse it, so the one sanctioned symbol is shown to be the only one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The four Elixir sabotage variables and the three Dart GC-count variables existed for recipes that are now dormant echoes, so they are scaffolding with nothing behind it. The tunables stay: DART_SOAK_SECONDS, JAVA_SOAK_SECONDS and their kind are the knobs each gate wakes with. The failure-message const also moves above wireFailure's own doc comment, which it had split. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The next red behind the zero-cost gate, and the control caught itself: #507 hardened the map's N-against-L check to compare unsigned, the way §3 requires every length, count and index on this wire to be compared, and the control's sed still named the signed spelling. Its own "the sabotage patched nothing" guard is what said so, which is the guard doing its job. The sabotage now names the current line and the gate goes red on three cases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 4, 2026
Merged
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.
Certification on main went red at bafdb69 (run 33887252419), the first full
Certify after #507 landed the id-table wire. Thirteen of fifteen jobs failed on
two causes.
1. The oracle panicked, so it was not an oracle
Every
testandinline-gateleg, andtables-cpp-release, died in the sameplace:
The cause. An array, map or keyed body is framed by its own
L, and thethree readers compute
end := r.off + bodyLenand later span the elements withr.sub(end - r.off). Between those two lines they read the body's header, theelement kind byte and the count, and the count is a canonical LEB128 read
against the PARENT buffer rather than against
end. A body'sLof 2 is theshortest that carries a header at all, but a LEB128 count is up to ten bytes,
so a count spelled wide leaves the cursor past the end its own
Lset and thespan goes negative. Go slices with it and panics.
The C++ reference has the same shape at all six of its sites, but its span is
an
int64_tsize on a reader whosehas()isoffset + bytes <= size, so anegative size reads nothing. It decodes no elements and reports malformed on
the first one it was asked for. That is the correct answer and the oracle now
gives it:
subTo(end)clamps the span at the cursor, so the body covers noelements instead of a negative number of them.
The fix is at the class, not the site. All three body readers go through
subTonow, so the invariant is stated once and named.The reference agrees on the mutant. Replayed alone, and across the whole
113652-mutant run: 0 divergences. There is no second defect here.
The mutant is pinned.
testdata/wire/tables/fuzz-vectors/is a new corpusof mutants the fuzzer has already gone red on, seeded into every run by name,
fed exactly as it is and before the random pass. It holds one vector,
stream_count_past_body. Pinned vectors are never mutated and the random passnever draws from them, so the mutant sequence is still a function of the corpus
and the seed alone and pinning one cannot move a red that was already there.
A red is now a seek. The run seed was already deterministic (
SEED ?= 24845619678in the Makefile), which is why this reproduced on the first trylocally, but the failure message printed neither it nor the mutant. It prints
both now, the bytes as hex up to 4 KB and the SHA-256 above that, so a red in a
CI log a person cannot re-run is still a red they can replay.
The negative control is
make tables-wire-fuzz-oracle-negative-control,which joins
tables-wire-fuzz-negative-controland so runs inmake test. Itremoves the clamp from a copy of
decode.gothrough a Go build overlay andrequires the run to go red on the panic AND on the pinned vector by name:
2. The port gates that read the id-table corpus
tables-elixir-release,tables-dart-release,tables-java-releaseandtables-js-releasefailed on one cause: they run targets that hold a port'scodec to
testdata/wire/tables, which is the id-table form now, while the portstill writes the wire's previous form.
tables-cpp-releasewas not a fifthcause. It was the wire fuzzer above.
#507 already ruled on this shape and spelled it as dormancy: the conformance
harness reports those ports'
wire,reportandjson-*surfaces ABSENT, andeach port's
conformance-negative-control-<lang>is a dormant echo namingissues #511 to #518. What was missed is every gate that calls the port's wire
codec directly rather than through the harness. This applies the same rule to
those, by name, citing the same issue per port.
It reaches further than the release legs.
make testruns the port legsafter the wire fuzzer, so the panic above masked them: with the oracle fixed,
test-c,test-cs,test-go,test-jsandtest-javaall go red on the samecause. Those are fixed here too, or the run would only fail later.
AllocsPerRungates andTestSoakintest/go-tablestables-c-soak,tables-c-soak-negative-controltables-cs-leg(the inlinedotnet runintest-cs, now a named target)tables-dart-soak,-soak-negative-control,tables-dart-alloc,-alloc-negative-control,tables-dart-usagetables-elixir-alloc-audit,-alloc-pin,-alloc-negative-control,tables-elixir-soak,-soak-negative-controltables-js-leg,tables-js-json-differential,-json-differential-negative-controltables-java-alloc,-alloc-negative-control,tables-java-soak,-soak-negative-controlEach dormant target keeps the comment that says what it is and gains the same
one-line echo #507 wrote, so
grep dormant make/*.mkis the wake list for#511 to #518.
3. The zero-cost gate, which was the next red behind the panic
With the oracle fixed,
make testreachestables-zero-costand it fails:This is #507's too, and it is a false positive rather than a leak. The reserved
node-table id is in every generated unit because every reader of the id-table
form owes §3.1's refusal of that id inside a NESTED body, whether or not its own
closure carries a pointer: the body it is handed may have been written by a unit
that does (§4). What a pointer-free unit pays for the rule is one
static const uint64_tand one comparison, which is none of the things the gate's own commentsays it holds (no builder, no arena, no handles, no lifecycle surface, no extra
descriptor columns).
The scan is BY SYMBOL now rather than by line,
TableNodeis matched with itswhole spelling so a node symbol nobody has written yet is still refused, and
exactly one spelling is sanctioned by name. Across the whole pointer-free scan
set that constant is the only
TableNodespelling present, so nothing else islet through.
The negative control,
make tables-zero-cost-negative-control, is a newtarget in
make test: it plantsTableNodeMapin a copy of a scanned headerand requires the same scan to refuse it.
Two things noticed and not fixed here
tables-js-soak,tables-js-allocandGo's
TestLoadAllocatesNothingload a golden the port now refuses andmeasure the refusal instead of the read path. They are green, so they are not
this red, but they are measuring nothing until the port wakes.
test/tables/wire_fuzz_main.cpphas nocodec for the
mapsunit, somapFieldis fixed here by inspection and bythe class rather than by the fuzzer. Adding the unit to the leg would close
that.
What was run
make testlocally, one C++ build at a time.bench/LOCKwas read first andno locked path is touched: the diff has nothing under
internal/codegen/c/,internal/codegen/cpp/,generated/c/,generated/cpp/orgenerated/c-ludicrous/.