Skip to content

certify: the tables bench corpus after #507, and the six control lines that are not failures - #542

Merged
gafferongames merged 2 commits into
mainfrom
certify-bench-corpus-507
Sep 4, 2026
Merged

certify: the tables bench corpus after #507, and the six control lines that are not failures#542
gafferongames merged 2 commits into
mainfrom
certify-bench-corpus-507

Conversation

@gafferongames

Copy link
Copy Markdown
Contributor

Certify on main went red again at 64325e1 (run 33893969798), in all eight
test and inline-gate legs. All eight stop at the same line, and it is one
line
, ./build/schema_test_bench_table:

record 2 is 2147 bytes, record 0 is 2139 — the table wire elides a field at its
default, so a varied value landed on one. Fix the vary mapping, not this check.

The six alarming lines in that log are negative-control output

They are gates going red on purpose, each immediately followed by its own
control announcing success and the chain continuing. Reading them as failures
would have sent this PR after defects that are not there, so here they are with
the line that follows each in the same log:

the line what printed it the next line in the log
FAILED: an opened block reports a used extent outside [ … ] tables-block-fuzz-extent-negative-control block fuzz extent negative control: removing that check from EVERY emitter turns the fuzzer red
FAILED: an opened block carries a count past its DECLARED MAXIMUM tables-block-fuzz-maximum-negative-control block fuzz maximum negative control: removing that check from EVERY emitter turns the fuzzer red
hostile_test.go:69: FAILED: cook-check panicked on a forged file (slice bounds out of range [8796093022236:176]) tables-cook-fuzz-negative-control make tables-cook-open — the target had SUCCEEDED
FAIL blob_span_main.cpp:73 blob past the slab: intact tables-blob-span-negative-control negative control: a blob larger than a slab, denied its span, overruns the slab
FAILED: a forgery OPENED that §7 says Open refuses: a data length one byte long tables-cook-open-lengths-negative-control negative control: removing the lengths check turns the cook forgery battery RED
FAILED: a data part too short to hold the root OPENED … tables-cook-open-root-negative-control negative control: removing the root check turns the cook forgery battery RED

The cook-check panic is worth spelling out, because a panic on hostile bytes is
exactly the shape of the defect #534 fixed one layer up. It is not one here.
tables-cook-fuzz-negative-control inserts return nil at the top of
checkDirectory through a go test -overlay, which removes cook-check's PASS
ONE — the directory scan — and then REQUIRES the hostile battery to go red. With
the scan gone, a forged directory offset reaches a slice and panics, and that
panic is the control's whole point: it is what proves the scan is the thing
standing between a forged file and that slice. No tracked file is edited, and
the unsabotaged battery is green:

$ go test -count=1 ./internal/tablecook/
ok  	github.com/mas-bandwidth/schema/v2/internal/tablecook	0.326s

So the cook and block readers refuse what §7 says they refuse, and this PR does
not touch them.

The one real failure: the tables bench corpus

The producer's own refusal named it, and the diagnosis is one step past what the
message says. Nothing landed on a default. A record's TRAILER carries one
eight-byte entry per distinct id the record uses, and #507 made an enum ride as
its VARIANT's id. The eight entity slots each drew a weapon at random, so every
record got its own set of distinct variants and its own trailer length. The
lengths came out in eight-byte steps, 2123 / 2131 / 2139 / 2147, one step per
entry the draw added or dropped.

The weapon is structure under this wire, so the slot's own index picks it now:
the eight slots take Fists through Grenade in every record, the set of ids
is fixed, and the value still differs from slot to slot. It is the rule the
pinned union arm already followed. A flags needs no pin, because TableDamage
rides as raw bits and names no id.

The goldens re-pin with it. They were still the previous form at 2391 bytes,
which is the second thing #507 left behind: the producer refused on the vary
mapping before it ever reached the byte compare.

bench table corpus OK: 64 records of 2147 bytes

The negative control is the draw put back, with slot kept in use so the
compiler's own -Wunused-parameter is not what answers:

$ sed 's|e.weapon = (TableWeapon) ( 1 + ( slot % 15 ) );|(void) slot; e.weapon = (TableWeapon) in_span( 15 );|'
record 2 is 2147 bytes, record 0 is 2139 — the table wire elides a field at its
default, so a varied value landed on one. Fix the vary mapping, not this check.
exit=1

The gate is make bench-table-check, and it already rides make test.

And the gate the re-pin exposed

tables-elixir-bench-gate was green against a corpus still in the PREVIOUS
form, which is the form the Elixir port writes, so it was holding that codec to
bytes it happened to agree with rather than to the wire the reference writes.
With the corpus current it refuses in its own words:

FAILED: variant round-trip bytes differ — refusing to bench a codec that does not reproduce the corpus

That is the rule tables-elixir-alloc-audit and tables-elixir-soak already
take, so it takes the same dormancy and the same issue, #515. It is the only
bench gate affected: every other port's bench/tables/<lang>/leg is a compile
gate in make test, and only Elixir runs leg run --gate.

What was run

make test locally, one C++ build at a time, and the pieces this machine cannot
reach (test-dart, test-elixir and the all-leg conformance step, for want
of a Dart SDK and a BEAM) are covered by a Certify dispatch on the branch.

gafferongames and others added 2 commits September 5, 2026 03:17
…-table wire

The next red behind the map fit control, and the producer's own refusal named
it: "record 2 is 2147 bytes, record 0 is 2139 — the table wire elides a field
at its default, so a varied value landed on one. Fix the vary mapping, not this
check."

The diagnosis is one step past what that message says. Nothing landed on a
default. A record's TRAILER carries one eight-byte entry per distinct id the
record uses, and #507 made an enum ride as its VARIANT's id, so the eight
entity slots drawing a weapon at random gave each record its own set of
distinct variants and its own trailer length. Record lengths came out in
eight-byte steps: 2123, 2131, 2139, 2147, one step per entry the draw added or
dropped.

The weapon is structure under this wire, so the slot's own index picks it: the
eight slots take Fists through Grenade in every record, the set of ids is fixed,
and the value still differs from slot to slot. It is the rule the pinned union
arm already followed. A flags needs no pin, because TableDamage rides as raw
bits and names no id.

The goldens re-pin with it. They were the previous form and 2391 bytes, which
is a second thing #507 left behind: the producer refused on the vary mapping
before it ever reached the byte compare.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Re-pinning the tables bench corpus to the id-table wire exposed it. The gate
was green against a corpus still in the previous form, which is the form this
port writes, so it was holding the codec to bytes it happened to agree with
rather than to the wire the reference now writes. With the corpus current it
refuses in its own words: "refusing to bench a codec that does not reproduce
the corpus."

That is the same rule as tables-elixir-alloc-audit and tables-elixir-soak, so
it takes the same dormancy and the same issue.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@gafferongames
gafferongames merged commit 80d6f55 into main Sep 4, 2026
26 of 35 checks passed
@gafferongames
gafferongames deleted the certify-bench-corpus-507 branch September 4, 2026 17:27
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.

1 participant