seams: a record inside a packet, the cook's own header, Elixir's bits consumed, §4.8 struck - #973
Draft
gafferongames wants to merge 2 commits into
Draft
gafferongames wants to merge 2 commits into
gafferongames wants to merge 2 commits into
Conversation
… consumed, §4.8 struck Four rulings, mostly docs with two small code items. (8) A FIXED RECORD INSIDE A PACKET. §3.4 had a three-carrier framing table and §3 said "no form byte, no header and no layout of its own" — which left whether the eight-byte hash rides, and who validates, unwritten. It rides: the record is the record exactly as it sits in a file, hash + body, with no form byte, no file header and no layout bytes, and the packet's reader validates it exactly as §5.3 validates a file's, refusing by the same names. Written into §3.4's framing table as a fourth carrier row, into §3's packet bullet, into FIXED-FORM-ALGORITHM §2 and §5.3. No packet emitter has a fixed-record field kind today and the checker refuses the field one rule earlier, so the rule is ahead of the code: recorded as §5.9 #49. (9) A FIXED COOK IS NOT A FORM-3 RECORD. The sentence justifying the sixteen pad bytes by the cook and the block form joining the registry under the same header is struck, in §3.4 and in §3's twin of it. The cook keeps its 64-byte header with SCHMCOOK at byte 0 and the block form its SCHMABLK prologue; a magic that must be read bytewise at offset 0 cannot share a header with a form byte at offset 0. New §3.4 block THE THREE PROJECTIONS states the three as independent flattenings. No second reason for the padding remains — the records do not begin at a multiple of sixteen, the layout between them and the header being variable-length — so it stays for wire stability and nothing else. (13) ELIXIR BITS CONSUMED. The packet reader discarded bits_read where §5 says Read reports it. read_<name>_bits/2 now returns {:ok, value, bits_read} | :error and read_<name>/2 delegates to it with the count dropped; both are emitted for every type and every union, the verdict and refusals identical. The name is claimed in internal/check's registry beside read_<name>, so a `type FrameBits` beside a `type Frame` is refused rather than silently taking the binding. test/elixir frames two objects in one buffer and reads the second from the ceiling of the first's count, including a seven-bit union where the count is not a whole number of bytes. SPEC §6.1 carries the Elixir row. (14) §4.8's PACKET RULE FOR NON-`type` UNION ARMS IS STRUCK. No leg implemented it and no `type` body can declare such an arm, so the page said what nothing read. One sentence remains: such arms are refused by name today and the wire rule is unassigned until a schema needs it. The refusal is already pinned by internal/check's TestTableRefusals and TestDiagnostics and by TestScalarArmInTypeBodyIsRefusedByCheck; the pages test now pins the strike so the rule cannot come back quietly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 11, 2026
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.
Four RULINGS from Rowan, final. Mostly docs, with two small code items.
(8) A fixed record inside a packet — the hash rides, and the packet's reader validates it like a file's
docs/FIXED-FORM-ALGORITHM.md§2 said only that inside a packet no form byte is written, anddocs/SPEC-TABLES.md§3 said "no form byte, no header and no layout of its own" — which left two things unwritten: whether the eight-byte layout hash rides, and who validates the record.Ruled: the record rides as the record exactly as it sits in a file, hash + body — no form byte, no file header, no layout bytes. The layout is not on the wire; the reader takes it from its own lineage, named by the record's hash. The packet's reader validates that one record exactly as §5.3 validates a file's — select by hash, the floor, the byte comparison (against the lock's own layout, there being no layout bytes to compare), the plan and record size, the pre-pass over the one record, the landing through the identity lane or the plan — refusing by the same names. A packet carries no count and no
rest, so §5.3 steps 1–4, 9 and 10 have nothing to do.Written into:
docs/SPEC-TABLES.md§3.4's framing table as a fourth carrier row ("INSIDE A PACKET"), and its heading is no longer "IN ALL THREE CARRIERS"docs/SPEC-TABLES.md§3's packet bullet, rewritten — it previously called the construct "the MESSAGE form by rule" and said no layout of its own, which read as the hash not ridingdocs/FIXED-FORM-ALGORITHM.md§2 and §5.3Does any packet emitter already carry a fixed record? No. There is no fixed-record field kind in any of the nine packet emitters — every layout-hash emitter lives in a
*tablepackage — andinternal/checkrefuses the field one rule earlier (atable,fixedor not, in atypebody is "is a table, not a wire type"; pinned byTestTableRefusalsandinternal/check/fixedclosurerows_test.go). So the rule is ahead of the code, and it is recorded as a ruling: §5.9 #49.(9) A fixed cook is not a form-3 record
§3.4 justified the sixteen pad bytes "so that the bytes do not move again when the cook and the block form join the registry under the same header". Struck — here and in §3's twin of the same sentence, and in
ir/fixedform.go's comment.The cook keeps its own 64-byte header with
SCHMCOOKat byte 0 (§7.1) and the block form its generatedSCHMABLKprologue (§19.1). A magic that must be read bytewise at offset 0 to refuse a foreign byte order cannot share a header with a form byte at offset 0, so neither accelerator will ever adopt those sixteen bytes; what the registry reserves4and5for is a number and nothing else.New §3.4 block THE THREE PROJECTIONS ARE INDEPENDENT FLATTENINGS states the three with their first bytes side by side, and closes the memory note's open question ("fixed cook = form-3 record?") with no: the wire form's hash selects a layout out of a lineage and reads a stranger's bytes backward; the accelerators' magic refuses a foreign byte order and their build version a foreign build outright.
The pad bytes' real reason: none remains. The records do not begin at a multiple of sixteen — the variable-length layout sits between them and the header — so the padding buys no alignment today. It stays for wire stability and nothing else: those bytes are written and read by shipped legs.
(13) Elixir reports bits consumed
read_<name>/2returned{:ok, value} | :errorand threw the count away (_ = bits_read), where SPEC §5 says Read reports bits consumed.read_<name>_bits(data, num_bits)→{:ok, value, bits_read} | :errorread_<name>/2, now delegating to it with the count dropped — same verdict, same refusalstypeand everyunion(internal/codegen/elixir); the head wraps its guard when the one-line form is past the formatter's width, somix format --check-formattedstays greeninternal/check's registry besideread_<name>, so atype FrameBitsnext to atype Frameis refused rather than silently taking the same module bindingtest/elixir/suite.exsframes twoProbeHeaders in one buffer and reads the second fromdiv(bits_read + 7, 8)bytes in, asserts the count equalsmeasure_probe_header, assertsread_<name>/2is the same read with the count dropped, and repeats it on a seven-bit union where the count is not a whole number of bytes — plus a refusal vector that returns:errorwith no countdocs/SPEC.md§6.1 carries the Elixir row: the consumed size is a second entry and not a second argument, a binary carrying no position(14) §4.8's packet rule for non-
typeunion arms is struck§4.8 fixed the packet wire for a non-
typearm — "The packet wire's rule is nevertheless fixed, so no port guesses it" — and no leg implemented it, atypebody being unable to declare such an arm at all. One sentence remains: such arms are refused by name today and the wire rule is unassigned until a schema needs it.The checker already refuses them by name, in two places (
internal/check/check.go— thetype-body refusal andcheckTableArmsReached), pinned byTestTableRefusals,TestDiagnosticsandTestScalarArmInTypeBodyIsRefusedByCheck. What had no pin was the page, soTestPagesPlaceTheVoidArmOutsideTheTableClosureClassnow pins the strike in both directions — the struck sentence must be gone and the new one present — so the rule cannot come back quietly.Gates
gofmt -l .— cleango test ./compiler/ ./internal/check/ ./ir/ ./internal/goldens/— green (-run 'DocExamples|Pages'included)mix format --check-formattedover every generated Elixir file — greentest/elixir/main.exs(the packet subset, with the new bits-consumed test) — OKgenerated/elixir,generated/elixir-ludicrous,generated/bench/elixir,testdata/golden/{elixir,ludicrous/elixir,packet-wide/elixir}Follow-up, not in this PR
Four table-leg runtime emitters still repeat the struck justification in a comment they emit into generated code (
internal/codegen/{ctable,cpptable,darttable,elixirtable}/fixedruntime.go). Fixing them churns each leg's committed table goldens, which is a wider regen than this PR's scope; the Go-side comment inir/fixedform.gois fixed here.🤖 Generated with Claude Code