The projection sees every reorder, and the codec law: enum, flags and union arm names in declaration order, ProjectionVersion 2 and a wire-law line (#462, #491, #463) - #488
Merged
Conversation
gafferongames
added a commit
that referenced
this pull request
Sep 4, 2026
Co-authored-by: Rowan Claude <rowan@mas-bandwidth.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
gafferongames
force-pushed
the
projection-variant-order
branch
from
September 4, 2026 05:30
98d7470 to
d165a21
Compare
Three defects, each a spurious MATCH on the packet wire — the direction the projection exists to refuse — and one change, because each moves every protocol id and the ids move once. 1. VARIANT ORDER (#462). An enum rendered as `enum Grade max=3 storage=8 variants=3` and a flags declaration as `flags Perks wirebits=3`, carrying nothing per variant, so reordering either left the id unchanged while every ordinal and every bit position changed meaning. Each declaration's variant names now enter the projection in declaration order. 2. UNION ARM NAMES (#491). Arm order rode in the arm types, which carry it only while the arms differ in type: two arms of one payload type reordered with every projected type unmoved, so tag 1 meant `left` on one build and `right` on the other under a single id. Arm names now project beside the arm's own facts. 3. THE CODEC LAW (#463's projection half). Nothing in the projection stood for the compiler's own encode and decode rules, so a change that moves bytes under an unchanged shape — the 2026-08-15 fixed-point rounding amendment did exactly that — left every id where it was. `WireLaw` rides the projection's second line at 1, and bumps on any compiler change that can alter, for the same schema and values, the encoded bytes, the accepted inputs, the rejected reads, the materialized defaults or a numeric conversion. The consequence of 1 and 2, stated on both pages: a variant or arm RENAME moves the id, which the deploy-together rule makes free. `ProjectionVersion` goes to 2 — the rendering describes what version 1 could not. Gates: an enum reorder, an enum rename, a flags reorder, a flags rename, a union reorder, a same-typed union arm reorder and an arm rename each move the id; the id is exactly the digest over the projection text and the same text under the next law number is a different id, for every corpus unit. Three negative controls, each surgical: without the variant lists the enum and flags gate goes red and the union gates stay green; without the arm names the union gates go red and the variant gate stays green; without the law line both law gates go red and the variant gate stays green. §20.8's battery loses its isolating control for the cook projection's union vocabulary — an arm rename now rides group 1 like every other vocabulary row — so a TABLE-ARMED union's arm rename takes its place: no packet wire (§2.6), no protocol id move, and the build version must move through group 3 alone. Re-pinned once, deliberately, on top of the maps release's own re-pin: every golden, the generated trees, the cooked conformance artifacts, the two block images' build_version word, and the build versions docs/USAGE.md and docs/SPEC-TABLES.md §7.3 print. §7.3's number was already stale on main — the cook form's bump to 2 moved it and left the page behind — and it takes the current value here.
gafferongames
force-pushed
the
projection-variant-order
branch
from
September 4, 2026 05:39
d165a21 to
d11254e
Compare
This was referenced Sep 4, 2026
gafferongames
added a commit
that referenced
this pull request
Sep 4, 2026
* bench/LOCK: the carve is spent, the prefixes are back #488 and #489 merged under the suspended prefixes. The lock re-freezes at the new reference; this diff is the file alone. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * bench/LOCK: keep the narrative line commented Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Rowan Claude <rowan@mas-bandwidth.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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 #462. Closes #491. Closes the projection half of #463; its differential gate stays owed and stays on the page's owed list.
Three defects, each a spurious MATCH on the packet wire — the direction the projection exists to refuse — and one PR, because each moves every protocol id and the ids should move once. Rebased onto
f762d6c(the maps release, the lock carve and the enumCountexport), with every derived artifact REGENERATED rather than hand-merged.The three defects
1. The projection could not see a variant reorder. An enum rendered as
enum Grade max=3 storage=8 variants=3and a flags declaration asflags Perks wirebits=3, carrying nothing per variant. Reordering either left the id byte-identical while every ordinal and every bit position changed meaning: a designer alphabetizing an enum shipped a build that shook hands with the old one and misread every value of it.2. The projection carried no codec law. A compiler change that moves bytes under an unchanged shape left every id where it was — the 2026-08-15 fixed-point rounding amendment (ties toward +infinity to ties away from zero) did exactly that, and two builds either side of it hold the same id and disagree about bytes on exact negative ties.
3. A union with two arms of the same payload type reordered invisibly (#491). Arm order rode in the ordered payload types, which carry it only while the arms DIFFER in type.
union Held { left Arm right Arm }reordered projects identically, so tag 1 meanslefton one build andrighton the other under a single id — the enum defect exactly, in the one shape the payload types cannot describe.The same probes under
origin/main's compiler and this branch's, measured today:The rule
Order is the wire, and the names carry it. Every enum's and every flags declaration's variant names, and every union's arm names, now enter the projection in declaration order:
That is the only way a reorder can be seen, so the consequence is accepted deliberately and stated on both pages: a variant or arm rename moves the id too, and the ship-together rule makes it free.
WireLawrides the projection's second line, at 1. It bumps on any compiler change that can alter, for the same schema and the same values, the encoded bytes, the accepted inputs, the rejected reads, the materialized defaults or a numeric conversion, and every protocol id in existence moves with it. The invariant it holds: no generated byte and no read decision may change for the same schema and input without the protocol id changing.ProjectionVersiongoes to 2. The rendering describes what version 1 could not — the ordered names of all three vocabularies — and the number says so. The ids move once for the whole PR either way.The id moves — one deliberate move, and nothing else
examples0x91a8e85156dfe2b1→0x682e2a15a56b78bf0x69d6a810cfa22717→0x68ee62213126f184examples1280x42050541a90eea8a→0x3a9a972a02c9e7ca0xedde8274fbab7f85→0x44a8123c94d09353tables/examples0x9924bf6d375ec24d→0x91df5fddd8edc1840x3cf5cc8b611ce26a→0x0d48f2a53a826273tables/pointers0x79242c6a555d3a60→0x2ce00b66ca6ca57c0x8b3fa61285c77990→0x94b679c0a547181btables/block0x7a706bf2ea47d94b→0xccb9f0129fbc0ebe0xe9c3dec8287989af→0x0adbc6e7f9c4605dtables/blockhome0xa6612e5b3f07080a→0x0fc1a1667e54b1440xf0348a7d88321955→0x9ebe680f43132468tables/messages0x8248fac3393e5575→0x4a7939657edfa4ab0x5d6070501121763a→0x2e7e8c6b14f7f107tables/stream0xbf90766c51a5100c→0x0da6b010d5e2ade70x093e4bddde5575de→0x4556b3579a442505tables/blobs0xec78e4ce7f5dd962→0x1cc1b12ef296a1f60xc20a8501aaedb152→0xe7a627afef0d063ftables/scalars0x01ba84a4cbd2143a→0xaa2ae3fa9f005efc0x60b4cb8aaf8eaec4→0xa8b581f9ab3e8580bench/corpus/Bench.schema0xae3b1e28b96e4586→0x5b8227d21cba8abf0x86eac54d593e599f→0xac9196746c84b699bench/corpus/RealWorld.schema0x8f7228a19854fbb2→0x0eefe6bcd25961390xa82854f1174c0b7a→0x2c22ca15bc43579ebench/corpus/BenchTable.schema0x1a38ac487752b0ae→0xd093e62e6f907c360x1864d106cc5f2c11→0x5f2254539e3ad47dEvery build version moves because the cook projection carries the protocol id in whole (SPEC-TABLES §20.1 group 1).
Where those numbers are pinned, and nowhere else:
testdata/golden/, 123 files:id.txt,ludicrous/id.txt, the tenbuild-version/*.txt, and every language's generated source. Re-pinned withgo test ./internal/goldens -update -run TestGolden; every changed line is an id or build-version line.testdata/golden/tables/, 31 files: the frozen Table pins the block zero-cost gate compares against. Edited by hand to the new ids ONLY — see the note on stale goldens below.generated/, 166 files: regenerated from the stamps CI derives. Every changed line is aProtocolId/PROTOCOL_IDbanner or constant, or aBuildVersionconstant.testdata/conformance/tables/cook-write/, 84 cooked artifacts:make conformance-generate. Verified byte by byte — every one of the 84 differs ONLY in the 8-bytebuild_versionword of its header, offsets 8..15, and in no other byte.docs/USAGE.md: the workedschema build-version tables/block/answer,0xe9c3dec8287989af→0x0adbc6e7f9c4605d(held byTestUsagePageBuildVersion).docs/SPEC-TABLES.md§7.3: the cook worked to the byte,0x4efe97313c704bb5→0x355ef4922f004a7f, its header hexdump line with it. That number was ALREADY stale on main: the cook form's bump to 2 in tables: maps in the language — the spelling, the key rules and the generated entry (#380) #487 moved it and left the page behind. Reproduced from the page's own schema, so the page computes again.internal/check/projection_test.go: the protocol-free neutrality probe,0xad54eaab53f241b4→0x0d68b71928bcbcf0(unchanged by the rebase — the neutrality probe declares no table), with its full projection text pinned beside it.testdata/wire/tables/block_render.binandblock_padded.bin: the two committed block images the conformance manifest names. A block's prologue is magic, build version, byte order (§19.1) andOpenis a header match, so both images take blockdemo's new number: byte offsets 8..15 in each,0xe9c3dec8287989af→0x0adbc6e7f9c4605d, and no other byte in either file. Missing these on the first push is what turned all nine conformance legs red — every leg refused a block whose prologue named a build that no longer exists, which is the header match doing its job.bench/LOCKis untouched: the old ids inside it are the narrative of PR #348's carve, not pins.Gates, and the controls that prove them
New in
internal/check/projection_test.go:TestIdMovesUnderVariantOrder— an enum reordered, an enum variant renamed, a flags declaration reordered, a flags variant renamed: each moves the id.TestUnionIdMovesUnderSameTypedArmReorder— two arms of one payload type swapped, every projected type unmoved: the id moves. This is A union with two arms of the same payload type reorders invisibly: arm names must enter the projection in order, as variant names now do (#462's sibling) #491's case, and the arm names are the whole of the difference.TestUnionIdBehaviorgains "an arm renamed" among the moves; its old stable row is gone, because arm names are wire facts now.TestWireLawLineMovesTheId— the projection opens with the rendering version and then the law line; the id is exactly the digest over that text; the same text under the next law number digests differently.TestIdIsStableUnderNonWireEditsloses its "an enum variant renamed" row, which is now a wire edit.New in
internal/goldens:TestWireLawBumpMovesEveryId— for all ten pinned units, the id is the digest over the projection and the same text under the next law number is a different id. No unit can sit out a bump.TestExportedSurfaceMovesNeitherWire, the gate #489 landed with the enumCountexport, takes new literals:examples 0x682e2a15a56b78bf / 0x68ee62213126f184andexamples128 0x3a9a972a02c9e7ca / 0x44a8123c94d09353. Its own comment names this as the legitimate case — "what legitimately moves them is a change to a PROJECTION itself … which moves the corpus goldens in the same commit" — and the goldens move in this commit. Measured before and after the rebase,Countmoves neither wire: every id here is what it was on the previous base.New in
ir/buildversion_test.go:TestBuildVersionSeesATableArmedUnionArmRenamed. §20.8's battery used a type-armed union's arm rename to isolate the cook projection's ownunion=/payload=tokens; that row now rides the protocol id like every other vocabulary row, so it can no longer isolate anything. A TABLE-ARMED union can — it has no packet wire (SPEC-TABLES §2.6), so the protocol id cannot see it — and the new test holds both halves: the arm rename moves no protocol id and must move the build version.Three negative controls, each run red then green, in
make testand on the pull-request gate:make projection-variant-order-negative-control,make projection-wire-law-negative-control,make projection-union-arm-order-negative-control. Each sabotages a COPY of the rendering throughgo test -overlay, so no tracked file is written, and each checks that the OTHER gates stay green — a control whose sabotage reddens everything says "something broke", not "this broke".Without the enum and flags variant lists:
and the union gates stay green under it.
Without the union arm names:
and the enum and flags gate stays green under it.
Without the codec law line:
and the variant-order gate stays green under it.
ir/buildversion_test.goalso moves a flags variant reordered and one renamed from the build version's exclusions to its meaning group. They move now, through the protocol id, which the cook projection carries in whole — the direction #435 owes the cook projection its own bit positions for.The pages
WireLawstated with its bump rule.What must ride with the release
A minor with a wire bump: every protocol id in existence moves once, and deployed peers refuse newly built ones rather than misread them. The release notes state the bump FIRST, in this order: every id moves; rebuild both ends and redeploy together; a variant or union arm rename now moves the id, so a spelling fix is a redeploy; nothing else in your schemas changed.
Two things for the owner
cpp-lockis carved (bench/LOCK: one-shot carve for #488 and #489 #495,bench/LOCK's prefixes suspended), so the generated C and C++ id lines pass. The emitters are byte-identical all the same —git diff origin/main...HEAD -- internal/codegen/c internal/codegen/cppis empty.testdata/golden/tables/is still stale on main, and not by this PR. Building origin/main's own compiler and regenerating the pins showsStreamTable.hand others differ in the tolerant read's unknown counting — PR Table reads are untrusted: the wire fuzzer with an independent oracle, C++ reference first (#391) #429 moved the C++ table emitter without re-pinning, sotables-block-zero-costinmake testis red on main today. This PR deliberately edits only the id lines in those files, so the staleness stays visible and separately fixable rather than being swept into a wire-bump diff.Verification
make check,go test ./..., all three negative controls, and the stamp-derived regeneration leaving a clean tree — all green on this branch, rebased onf762d6c. No C++ was built here (other builders share this machine); the C++ chain is certify's.CI state is reported in the thread below.