Skip to content

packet wire: payload-free arms enter the nine-leg corpus - #960

Draft
gafferongames wants to merge 6 commits into
fixed-table-formfrom
rowan/packet-void-arms-nine
Draft

packet wire: payload-free arms enter the nine-leg corpus#960
gafferongames wants to merge 6 commits into
fixed-table-formfrom
rowan/packet-void-arms-nine

Conversation

@gafferongames

@gafferongames gafferongames commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

docs/SPEC.md §4.8: "All nine backends carry payload-free arms on the packet wire" and "A payload-free arm is the tag alone". The only suite was test/packet-void/ — C and C++, with its own packet-void-{c,cpp}-{read,write} controls. No schema under examples/, examples128/ or examples-wide/ declared a bare-name arm, so the construct never entered the golden-source, wire-oracle, cross-language byte-identity or golden-wire gates on Go, Rust, C#, Java, Dart, JS or Elixir. (examples/Clauses.schema's EmptyUnion is arms-of-empty-types, which §4.8 distinguishes.)

What lands

examples/VoidArms.schema — the shared nine-leg corpus (SCHEMAS := $(wildcard examples/*.schema), so it enters generation and the golden pins with no Makefile edit):

  • union VoidSignal { ping; pulse VoidPulse; level VoidLevel; ack } — two payload-free arms bracketing two typed ones, so an emitter that special-cases the first or the last arm is caught.
  • type VoidSignalPacket carries it behind a 3-bit lead, so the tag is not byte aligned: a union field, a second union field selecting the other bare arm, and a counted array of unions.

Its own file, not an arm on Wire.schema's ProbeShape. That union's 2-bit tag has exactly one out-of-set value, 3, and all nine legs use it as the read-refusal negative control. An arm there would make it legal and take the teeth out of nine controls. VoidSignal has four arms, so its tag is 3 bits for [0, 4] and 5..7 stay out of set.

Per-leg byte identity

No compiler change was needed: every emitter already carries the construct, and each writes the tag in 3 bits and nothing else for ping and ack — the arm costing exactly what tag 0 costs, with the C++ reference as the oracle.

leg generated bare arm tag width VoidSignalMaxBits
cpp (oracle) generated/cpp/VoidArms{,Wire}.h tag only 3 19
c generated/c/VoidArms{,Wire}.h tag only 3 19
go generated/go/VoidArms.go tag only 3 19
rust generated/rust/src/voidarms.rs tag only 3 19
cs generated/cs/VoidArms.cs tag only 3 19
java generated/java/VoidArms.java tag only 3 19
dart generated/dart/VoidArms.dart tag only 3 19
js generated/js/VoidArms{,Flat}.js tag only 3 19
elixir generated/elixir/VoidArms.ex tag only 3 19

MaxBits = 19 = 3 tag bits + the largest arm (VoidPulse, 16) in every leg — identical across all nine, which is the width half of byte identity.

Goldens

go test ./internal/goldens -update re-pinned. The hunks are only the new construct plus the unit's protocol id: the new unit joins package example, so every file's id moves 0x8656ae68c06b97a70xcaa884d7062cccc8, and the C ExampleView registry gains the types and the union. testdata/wire/ is untouched — no existing fixture's bytes move.

Draft: what is NOT in this PR yet

  • No runtime wire pin. The nine per-leg mains (test/main.cpp, test/c/main.c, test/go/main.go, …) are hand-written and none constructs a VoidSignalPacket yet, so there is no testdata/wire/voidarms.bin for the legs to compare against. The byte identity claimed above is over the generated codecs, not over nine executed writers. The oracle case in test/main.cpp plus the eight comparisons against its pin are the follow-on.
  • No per-leg negative controls in the packet-void-c-{read,write} shape for the seven legs (make/negative-controls.json untouched).
  • Legs not run here: the nine-leg make test chain needs the sibling runtime checkouts and the pinned toolchains; only the compiler-side gates (./bin/schema check examples, go test ./compiler ./internal/check ./internal/goldens, gofmt -l .) were run on this bench.

🤖 Generated with Claude Code

Follow-up 97804e2 — two red gates, found by a DeepSeek read

A DeepSeek read of this branch found two gates it left red, and 97804e2 fixes both.

  • TestExportedSurfaceMovesNeitherWire/examples held the protocol id and build version as literals (0x8656ae68c06b97a7 / 0x3084078b2f7d571e) and they were not moved with the corpus. The goldens above were re-pinned; the literals, which exist precisely so a re-pin cannot silence the gate, were left behind. They now carry 0xcaa884d7062cccc8 / 0x68b43963ecc918e3 — the values this PR already pinned into testdata/golden/id.txt and testdata/golden/build-version/examples.txt. This is the allowance the gate's own comment grants: a change to a projection moves them in the same commit as the goldens. examples128 is untouched.
  • TestCorpusIsCanonical failed on examples/VoidArms.schema: the two typed arms of union VoidSignal carried two spaces in the type column where schemafmt writes one. make fmt canonicalizes it. Whitespace only, so neither hash moves — verified by a rm -f bin/schema rebuild and a nine-leg regeneration of generated/, which is byte-identical.

gafferongames and others added 4 commits September 11, 2026 08:26
docs/SPEC.md §4.8 says all nine backends carry payload-free arms on the
packet wire and that such an arm is the tag alone, but the only suite was
test/packet-void/ (C and C++). No schema under examples/, examples128/ or
examples-wide/ declared a bare-name arm, so the construct never reached the
golden-source, wire-oracle or golden-wire gates on the other seven legs.
(examples/Clauses.schema's EmptyUnion is arms-of-empty-TYPES, which §4.8
distinguishes.)

examples/VoidArms.schema puts it in the shared nine-leg corpus: two
payload-free arms bracketing two typed ones, so an emitter that
special-cases the first or last arm is caught, carried by a message behind a
3-bit lead (the tag is not byte aligned), in a union field, a second union
field, and a counted array of unions.

Its own file rather than an arm on Wire.schema's ProbeShape: that union's
2-bit tag has one out-of-set value, 3, and all nine legs use it as the
read-refusal negative control. An arm there would make it legal. VoidSignal
has four arms, so its tag is 3 bits for [0, 4] and 5..7 stay out of set.

All nine emitters carry it with no compiler change: each writes the tag in 3
bits and returns, the arm costing exactly what tag 0 costs. Goldens re-pinned
(the new unit joins package example, so every file's protocol id moves to
0xcaa884d7062cccc8; the C ExampleView registry gains the types and the
union). testdata/wire is untouched — no existing fixture's bytes move.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
gafferongames and others added 2 commits September 11, 2026 09:21
…dArms

Two gates this branch left red, both found by a DeepSeek read.

TestExportedSurfaceMovesNeitherWire pins the examples unit's protocol id
and build version as LITERALS, on purpose: a re-pin of testdata/golden is
the mistake it refuses, so a gate that re-pinned them would say nothing.
Its own allowance is that a change to a PROJECTION — here an addition to
the corpus, which is a change to the wire shape itself — moves them in
the same commit as the goldens. This branch moved testdata/golden/id.txt
and testdata/golden/build-version/examples.txt and left the literals
behind, so the gate failed on its own re-pin. The literals now carry
0xcaa884d7062cccc8 and 0x68b43963ecc918e3, the values the pinned goldens
in this same commit's parent already hold. examples128 is untouched: no
corpus file of its moved.

TestCorpusIsCanonical failed on examples/VoidArms.schema: the two typed
arms of union VoidSignal carried two spaces in the type column where
schemafmt writes one. `make fmt` canonicalizes it. The edit is
whitespace, so neither hash moves — verified: the ids above are what the
formatted corpus hashes to, and a nine-leg regeneration of generated/
off a rebuilt bin/schema is byte-identical.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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