Skip to content

docs: every page against the finished specification - #536

Merged
gafferongames merged 11 commits into
mainfrom
docs-pass
Sep 4, 2026
Merged

docs: every page against the finished specification#536
gafferongames merged 11 commits into
mainfrom
docs-pass

Conversation

@gafferongames

@gafferongames gafferongames commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

The docs pass against the finished specification. docs/SPEC.md and
docs/SPEC-TABLES.md as they stand on main at dbff2ff — after #507, #510,
#527, #529, #530, #531 and #532 — are the two sources of truth. Every other
page was read against them sentence by sentence and corrected where it
disagreed.

Out of scope by instruction: docs/TUTORIAL.md (being rewritten on the
tutorial branch, #520), and the two specification pages themselves.
No SPEC cross-reference edit was needed: no correction on any other page
exposed one.

Rebased onto main at 1711ee0, which carries #538's status fix for #537.

The cold read ruled "merge after named changes"; they are in the branch.
The second commit carries them and is summarised at the bottom of this body.

Two issues filed rather than fixed here: #537, now landed as #538, and
#540 (the [E.Max]T refusal in a table body is not made by the checker).


Corrections, by page

README.md — 1

  • the beside-list omitted COMPETITION.md, the only doc page nothing linked.

The README split stays. Both files keep their own opening line, their own
feature list and their own release stance. Neither was merged into the other,
renamed or deleted.

README-NEW.md — 7

  1. "each field carries an id, a kind and a length" is the wire before tables: the id-table wire (#435) #507. A
    field carries a REFERENCE to its name's id, a kind byte and its payload, and
    the file carries every id it used once each in a trailer (§3).
  2. "Every one of those events is counted in a read report" — an absent field
    taking its declared default and a field renamed under was are both SILENT
    (§4, §4.1's table). Only the skip and the clamp count.
  3. the silent class is FOUR edits, not two (§4.1): a changed default, a flags
    variant moved, a field's referent swapped for one that cannot stand in for
    it, and a fixed field's F moved.
  4. the cook's Open check list omitted the reserved words (§7's enumeration).
  5. a refused Open now names itself beside the null through
    TableRefuseReason (§7).
  6. the block form's layout contract is not "asserted at compile time in both
    languages": C++ has static_assert, C# has a check at type initialization
    (§19.3, and the C# note in §2's front matter).
  7. the descriptors carry the text key, the /// doc comment and the tags
    beside the columns the page listed (§8.1).

Plus PORTING.md, COMPARISON-TABLES.md and COMPETITION.md added to the
beside-list, which README.md already carried.

docs/USAGE.md — 14 corrected, 4 sections added

Corrected:

  1. "fields carry their name's hash" — a field is IDENTIFIED by the hash of its
    name; what it carries is a reference (§3, §5).
  2. "evolution-tolerant TLV" — the wire is the form byte, the
    reference-kind-payload header, canonical LEB128 numbers and a trailing id
    table.
  3. the tolerance list gained widened (§4).
  4. "only structural damage stops a load" — framing damage stops the damaged
    nesting level, keeps what it decoded and reads on past L (§4).
  5. "an enum value on the wire is the hash of its variant's name" — kind 30
    carrying a reference to that id; an arm header carries the arm id reference
    and a kind byte (§3).
  6. "an all-default table saves as 2 bytes" — TEN bytes: the form byte, the
    body's zero reference and the eight-byte entry count (§3).
  7. the protocol id "covers your type declarations and nothing else" — it is
    the CLOSURE over them, with the eight edges enumerated, and flags projects
    whether reached or not (SPEC §3.1, §3.2, spec: reachability-scoped protocol id, and retain-unknown (#524, #525) #527).
  8. [E.Max]T is now REFUSED in a table body by name, with [E]T the fix
    (§2.4, §11) — the page taught the respelling as a wire break only.
  9. "C++ carries the form today; every other backend refuses a unit that
    declares one" (a union of table arms) — all nine carry it: a union of table
    arms with no pointer is an ordinary member of the fixed class.
  10. "a pointer rides as a u32 index into a flat node table" — a canonical
    LEB128 node index under kind 17 (§3.1).
  11. the byte-buffer section gained *wstring, the third spelling (§2.5).
  12. BlockOpen names its refusal beside the null, bad_layout included
    (§19.2).
  13. the block form's "asserted by generated code in both languages" — the
    mechanism is per language: static_assert, a const assert, a check at type
    initialization, a refusing init(), an accessors-against-descriptors check.
  14. "string and bytes byte lengths and array counts ride in uint32" — every
    length, count and index is a canonical LEB128 with 64 bits of capability
    (§3). The scalar list gained wstring(N).

Added, because the page's doctrine is that a stranger can use every feature
without opening the specification, each carrying a status note where no backend
emits it yet:

  • wstring(N) (SPEC §4.12) — the bound in code units, the two-step wire
    with no alignment, the four reader rules and what no reader enforces, the
    write side's two checks, the per-target boundary cost, and kind 33 on the
    table wire.
  • Documenting and tagging (SPEC §4.1, §4.2) — why /// is opt-in, what the
    text is, where it goes, every refusal, and tags at every line kind, with both
    named as free edits.
  • Maps (§2.8) — the sorted entry array, the key set and every refusal, the
    order, the builder and const surfaces, Find and the optional index, the
    eight claimed names, the evolution events, and the [..N]Pair migration.
  • Unbounded arrays (§2.9) — []T and []*T, the same bytes as [..N]T,
    the element set and its four refusals, insertion order, the surface, and the
    two counters that cannot fire.

Also: the id-table wire's own backend status (the reference and the tool; the
eight ports on #511 to #518), and the table of contents.

Verified, no edit needed: lines 1012 and 1281 read "the same six counters",
which is the spec's own count after #532unknown, kind_mismatch,
widened, clamped, duplicate and the malformed flag.

docs/PORTING.md — 3

  1. M6 named the node-table reserved id as 0xFFFF; it is
    0xFFFFFFFFFFFFFFFF, which M20 on the same page already spells (§3.1).
  2. M6 called a pointer "a u32 node index"; it is a canonical LEB128 index.
  3. M18 said "the arm id is the only discriminator — no per-arm kind byte
    rides", and called a bad arm length a KIND MISMATCH. An arm header IS a
    field header (§3), so a retyped arm is the kind mismatch and a bad length is
    framing damage; the payload-free arm is kind 32; and the terminator
    control moves a one-byte zero reference, not a u16.

Verified, no edit needed: line 771's "six counters" is the spec's count.

docs/VERSIONING.md — 5 corrected, 3 rows and 2 owed entries added

  1. "a field is id, kind, payload; a node is type id, length, fields" — a
    reference names a slot of the id table, and a node record is a type id reference, length, body (§3, §3.1). The page's own layout section already
    said so, so the two contradicted each other.
  2. the projection's exclusion list now names doc comments, tags and const
    declarations, which SPEC §3.1 excludes explicitly.
  3. the tiny-message sharp edge names the message form beside the file form's
    cost, and the ten-byte empty table.
      1. two evolution rows added for the unbounded array, and one for a doc
        comment or a tag added, changed or removed — free in all three frames.
  4. two entries added to "Owed before 3.0.0": the unbounded array and its
    list_migrates golden, and the /// comment with its two descriptor
    columns.

The page was already current on the widening rule, the message form and
retain-unknown.

docs/PERFORMANCE.md — 0

Bench data in its recorded convention, with no spec-facing claim.

docs/FAQ.md — 6

  1. "only structural damage fatal" — nothing on the table wire is fatal; framing
    damage stops the damaged nesting level and the parent reads on (§4). The
    list gained widened.
  2. the Protobuf answer, per the owner's addendum: "If your client and
    server ship independently and must interoperate across versions over one
    connection, use Protobuf" is no longer a decline. That is what the TABLE
    wire is for and the message form is its stream shape, at 58 bytes against
    proto3's 49 on the same login (§3.3). What schema still declines is RPC, and
    the answer now says so.
  3. maps have landed and are no longer "landing in the C++ reference first".
  4. a bullet added: no unbounded collections on the packet wire, and why a table
    body takes both (§2.8, §2.9).
  5. the write-side idiom list omitted Elixir's ArgumentError in every build
    (SPEC §5).
  6. "Is everything supported in all nine languages? Yes" — wstring(N) is
    specified and emitted nowhere, so it is named rather than counted as parity.

docs/COMPARISON.md — 1

  • the Protobuf paragraph said only that this wire has no tags. It now names the
    table wire and the message form as schema's answer for the
    independently-versioned case, on the same addendum.

docs/COMPARISON-TABLES.md — 20

Named by the design read: row 162 (the read report list gained widened),
row 263 (a changed kind, with the widening exception stated), row 272
(the same list in the evolution table). Also line 163 and line 273, "the
silent-edit class enumerated: exactly five" — it is exactly four (§4.1).

Beyond those:

  • the encoding-model row said "byte TLV: id u16, kind u8, payload, u16
    terminator" — the three parts, the reference header, the canonical LEB128 and
    the trailing table.
  • field identity said fold16(fnv1a32(name)) — it is fnv1a64(name), at
    sixty-four bits, no fold, one rule for four vocabularies (§5).
  • the length-prefix row said every nested table, string, array and arm carries a
    u32 length; a LEB128, and an arm carries its own kind byte beside it.
  • the pointer-encoding row said "u32 node index" under a reserved id it did not
    spell.
  • size ceilings said "node body up to 4 GiB" — no ceiling in the wire at all.
  • the framing-overhead row gained the 1487-of-2391 split the ladder cites.
  • self-describing said "names ride as hashes" without saying where.
  • the enum row, the union row, the arrays row, the bytes row, the strings row,
    the optional-fields row, the maps row and the sorted-lookup row each carried a
    construct that has since landed or moved: kind 30, an arm as a field line,
    []T, *bytes/*string/*wstring, wstring(N), ?[..N]T, map[K]V and
    its Find.
  • the doc-comments row said "deferred, design pinned"; it is specified down to
    its refusals.
  • "Where they are ahead" rows 3, 7, 9 and 12, and the closure-gaps paragraph
    under them, listed as pending: maps, sorted lookup, doc comments, arrays of
    pointers, arrays of unions and optional arrays. Four are landed and the
    remaining gaps are named instead.
  • the text-form row said "landing with The text form for the VARIABLE class in the C++ reference and the tool: a tree reads and writes as nested tables, a shared node is labeled once with &node and named by that label after (#275) #388"; it landed.
  • one row added to the evolution table for the unbounded array.

docs/COMPETITION.md — 13

Named by the design read: row 268 (defined widening or promotion),
row 337 (the "Promotion" bullet under "Where they are ahead"), row 378
(footnote s24, the read report's counters).

docs/SECURITY.md — 1

  • the wire fuzzer's mutator list named "duplicate ids, kind swaps, node indices
    out of range"; it now names the id-table wire's own numbers — non-minimal
    LEB128, a reference past the entry count, the trailer passes, the moved body
    terminator, the arm kind and length, and the reserved kind 34 (§4.2).

The page was already current on wstring, kind 33 and kind 12.

docs/CONTRIBUTING.md — 0

No spec-facing claim disagreed.

examples/README.md — 0

Type-wire corpus only, and current.

bench/tables/README.md — 1

  • the pinned wire's framing split, 1487 of 2391 and 62%, which SPEC-TABLES'
    performance ladder cites FROM this page and this page did not carry.

test/conformance/README.md — 1

  • the completion tracker listed four missing CLASSES. One absence is wider: the
    id-table wire itself is the reference's and the tool's, the eight ports write
    its previous form, and their negative controls print dormant — with the
    eight issues and the PORTING M20 cell named.

The report surface's u,k,c,d,m line is left alone deliberately: it
describes the harness protocol as the tree implements it, and it gains
widened when widened lands.


Issues filed

  • SPEC-TABLES §2.9 claims a backend status the tree does not carry #537 — SPEC-TABLES §2.9 claims a backend status the tree does not carry.
    The section says "the C++ REFERENCE and the TOOL carry it" and names a
    tables/lists corpus of nine instances. Against main, table Save { scores []int32 } draws expected an expression, found "]", there is no
    tables/lists directory, and nothing under internal/ mentions an unbounded
    array. Every other subsection that landed ahead of its implementation says so
    plainly (§3.3, §6.6, SPEC.md §3.1, SPEC.md §4.12), so this line is an
    outlier. The issue also notes §2.5's status line covering *wstring, which is
    nowhere in the tree.

What was checked with the tool

go build ./cmd/schema and schema check over hand-written units for every
construct this pass added or corrected. That is how the status notes on
wstring(N), []T and the /// block are honest rather than assumed: maps,
general union arms and payload-free arms compile today; []T, wstring(N),
*wstring, a tag on a constant and a tag on a table declaration do not. No C++
was built.


The cold read's named changes

Landed on the same branch as one commit.

Rulings applied. The README split stays exactly as it is, on the owner's
standing rule. Status notes are USAGE's own practice, so every present-tense
claim of an implementation the tree lacks now carries the italic note rather
than standing bare. Em dashes are out of the NEW prose only, and left inside
sentences this pass merely corrected, per #402.

The twelve edits.

  1. USAGE, the union of table arms: the C++ reference and the tool carry it and
    every other backend refuses a unit that declares one by name (Table is king: the recursion closure — every slot that can hold a value can hold a table, at every depth, in every form #392, §15).
    It said "all nine".
  2. USAGE, maps and unbounded arrays: C++ and the tool, not "C++ and C", and
    each section now states its own backend status at its head instead of one
    trailing sentence for both.
  3. USAGE, the protocol id's closure: specified, and the compiler does not
    scope by it yet, ir.WireProjection still rendering the unit at
    ProjectionVersion 2 (A reachability-scoped wire-shape projection (ruling 11 on #523) #524).
  4. FAQ: one clause for the two things that DO stop a table read, a trailer the
    reader cannot read whole and a form byte it does not know, both of which
    decode nothing rather than mangle anything.
  5. README-NEW and USAGE: TableRefuseReason is specified and no backend
    writes it, so an Open answers the null alone today (Design lock-in before the sweep: the cold read's shortest list #523), matching
    COMPETITION.
  6. widened: one note per site, specified and counted nowhere yet (Design lock-in before the sweep: the cold read's shortest list #523), on
    USAGE twice, FAQ once and COMPARISON-TABLES three times.
  7. README-NEW: the doc and tags descriptor columns are specified and no
    backend emits them, so they are named apart from the columns that ship.
  8. USAGE: *wstring is specified and no backend emits it (tables: the wstring kind on the id-table wire #522, spec: §2.9 and §2.5 state their backend status honestly (#537) #538).
  9. USAGE and VERSIONING: the [E.Max]T refusal in a table body is stated by
    §2.4 and §11 and the checker does not make it. Noted at both sites, filed as
    check: the [E.Max]T refusal in a table body (§2.4, §11) is not made #540, and added to VERSIONING's "Owed before 3.0.0" list.
  10. test/conformance/README: what the HARNESS prints,
    N seeds absent (roots the leg has no codec for), rather than the
    dormant a make target echoes, which is a different instrument.
  11. USAGE: the front end refuses the wstring spelling at parse, said in the
    status note.
  12. FAQ: "everything in all nine: yes" is scoped to the PACKET wire, with the
    table wire's state in one sentence beside it, fixed class everywhere, the
    variable class the reference's and the tool's, and the id-table wire's
    eight ports on Go: the table wire's id-table form (#435) #511 to Rust: the table wire's id-table form (#435) #518.

Three one-sentence additions the new sections owed. A map's Insert
answers NULL for a key longer than N, and for an arena that cannot carve a
segment, because a truncated key would be a merged entry. A list's Add
answers NULL at the arena, its indices are not stable across an erase while
the POINTER Add handed back is the stable handle, and a count past the
int32 extent cap is refused. And the list claims three names against its
field, Add, Each and Erase, where a map claims eight.

Issues filed by this pass

rowan-claude and others added 9 commits September 5, 2026 02:49
README-NEW.md: the field header is a reference, a kind byte and a payload
with the ids in a trailer, and only the skip and the clamp are counted; the
silent class is four edits and not two; the cook's Open checks the reserved
words too and names its refusal beside the null; the block form's layout is
held by a static_assert in C++ and a type-initialization check in C#;
descriptors carry the text key, the doc comment and the tags. Both READMEs
link COMPETITION.md, and README-NEW links PORTING.md and COMPARISON-TABLES.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Corrected against the id-table wire (#507), the reachability-scoped
projection (#527), the refused positional keyed spelling, and the per-language
layout-contract mechanisms. Added the features a stranger could not use from
this page: wstring(N), the /// doc comment and tags, maps, and unbounded
arrays, each with its own status note where no backend emits it yet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The field-header sentence now matches the id-table wire; the projection's
exclusion list names doc comments, tags and const declarations; the evolution
table gains rows for the unbounded array and for annotations; the tiny-message
edge names the message form beside the file form's cost.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
M6 named the node-table reserved id as 0xFFFF and the node index as a u32;
both are M20's own facts and are now spelled from it. M18 said no per-arm
kind byte rides and called a bad arm length a kind mismatch; an arm header is
a field header, so a retyped arm is the kind mismatch and a bad length is
framing damage. The arm terminator control names the one-byte zero reference.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
FAQ: nothing on the table wire is fatal, and the widening event joins the
list; the independent-versioning answer is the table wire and its message
form rather than another library, with RPC named as what schema still
declines; maps have landed; the packet wire's refusal of unbounded
collections is stated; Elixir's write-side raise joins the idiom list;
wstring is named as the one type not yet emitted. SECURITY: the wire
fuzzer's mutator list names the id-table wire's own numbers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tion

The read report is six counters, not five; the silent class is four, not
five; a kind that grew decodes exactly and counts widened; the wire has no
size ceiling now that every length is a canonical LEB128. Maps, sorted
lookup, arrays of pointers, arrays of unions and optional bounded arrays are
closed rather than pending; doc comments are specified rather than deferred;
the unbounded array joins the evolution table. COMPARISON names the table
wire as the answer for the independently-versioned case.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ruling

The when-to-use-theirs line no longer declines the independently-versioned
service: that is what a table is for, the message form is what makes it
competitive by the byte, and the remaining gaps are cited as issues. Maps and
general union arms move off the not-built list; the widening rule, the doc
comment and retain-unknown are specified rather than declined or deferred;
the id-table wire's own facts replace the u32 lengths, the 16-bit hash and
the 4 GiB body, and the eight dormant port legs are named in the scoring
rule and in every footnote that turned on them.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
bench/tables: the framing split the performance ladder cites from this page.
test/conformance: the id-table wire is the reference's and the tool's, the
eight ports write its previous form, and their negative controls print
dormant until they carry it. USAGE says the same where it introduces the
form byte.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e form

Field identity is fnv1a64 with no fold, not a folded 32-bit hash; the
encoding model is the form byte, the reference-and-kind header, canonical
LEB128 and the trailing id table; a union arm carries its own kind byte; the
node index is a LEB128 under kind 17 and the reserved id is the full 64 bits.
Maps, sorted lookup, blob buffers and optional bounded arrays are landed
rather than pending; the unbounded array and wstring join the rows they
belong to; doc comments are specified rather than deferred.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Status notes on every present-tense claim the tree lacks: the protocol id's
reachability closure (#524), the [E.Max]T refusal (#540), wstring and *wstring
(#522), TableRefuseReason and the widened counter (#523), the doc and tag
descriptor columns, and the union of table arms (#392). Maps and unbounded
arrays state their own backend status per section rather than in one trailing
sentence, and the list is owed rather than carried.

FAQ scopes the nine-language answer to the packet wire with the table wire's
state beside it, and gives the whole-wire malformed case and the form-byte
refusal one clause, since both decode nothing. The conformance README says
what the harness prints, N seeds absent, rather than what a make target
echoes. Three additions the sections owed: Insert answers NULL for an
oversized key, Add answers NULL at the arena, and a list's indices are not
stable across an erase while its pointer is, beside the three names it claims.

Em dashes are out of the new prose: the four USAGE sections, COMPETITION's new
paragraphs, the conformance and bench additions and VERSIONING's new rows.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@gafferongames
gafferongames marked this pull request as ready for review September 4, 2026 16:55
The two unbounded-array rows and the maps row said what the construct is
without saying whether anything emits it, where every other page now does.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@gafferongames
gafferongames merged commit e676fc4 into main Sep 4, 2026
20 checks passed
@gafferongames
gafferongames deleted the docs-pass branch September 4, 2026 17:06
gafferongames added a commit that referenced this pull request Sep 4, 2026
…ntation (#539)

Several specification sections state present-tense behavior the tree does not
carry. Each now says so in the house form §3.3, §6.6 and §2.9 already use: the
behavior is specified ahead of its implementation, what the tree does today,
and the issue that owes it. Every line is deleted by the implementation PR
that lands the behavior. No rule changes.

- SPEC.md §3.1: the compiler reads no comment kind. A `///` line lexes as an
  ordinary `//` comment and is discarded, no doc text reaches the IR or a
  descriptor column, and none of §4.1's misplacement refusals fires. #523
  ruling 4.
- SPEC.md §4.2: a tag compiles on a `type` declaration alone. Every other line
  kind refuses one under a diagnostic written for a different rule. #523
  ruling 4.
- SPEC-TABLES.md §8.1: no target emits `doc`, `num_tags` or `tags` on
  `TableFieldInfo` or `TableTypeInfo`. #523 ruling 4.
- SPEC-TABLES.md §6.5, §7 and §19.2: `TableRefuseReason` is spelled nowhere,
  `Open` and `BlockOpen` take no reason out-parameter, a measure answers a
  bare `-1`, and the name is not claimed. #523.
- SPEC-TABLES.md §4: no report struct carries `widened`, and every pair the
  ladders accept is a `kind_mismatch` today. #523.
- SPEC-TABLES.md §2.4 and §11: `schema check` accepts `[E.Max]T` in a table
  body with no diagnostic and exit 0. #540, which VERSIONING.md's "Owed
  before 3.0.0" list already carries after #536.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
gafferongames added a commit that referenced this pull request Sep 4, 2026
…ntation (#539) (#541)

Several specification sections state present-tense behavior the tree does not
carry. Each now says so in the house form §3.3, §6.6 and §2.9 already use: the
behavior is specified ahead of its implementation, what the tree does today,
and the issue that owes it. Every line is deleted by the implementation PR
that lands the behavior. No rule changes.

- SPEC.md §3.1: the compiler reads no comment kind. A `///` line lexes as an
  ordinary `//` comment and is discarded, no doc text reaches the IR or a
  descriptor column, and none of §4.1's misplacement refusals fires. #523
  ruling 4.
- SPEC.md §4.2: a tag compiles on a `type` declaration alone. Every other line
  kind refuses one under a diagnostic written for a different rule. #523
  ruling 4.
- SPEC-TABLES.md §8.1: no target emits `doc`, `num_tags` or `tags` on
  `TableFieldInfo` or `TableTypeInfo`. #523 ruling 4.
- SPEC-TABLES.md §6.5, §7 and §19.2: `TableRefuseReason` is spelled nowhere,
  `Open` and `BlockOpen` take no reason out-parameter, a measure answers a
  bare `-1`, and the name is not claimed. #523.
- SPEC-TABLES.md §4: no report struct carries `widened`, and every pair the
  ladders accept is a `kind_mismatch` today. #523.
- SPEC-TABLES.md §2.4 and §11: `schema check` accepts `[E.Max]T` in a table
  body with no diagnostic and exit 0. #540, which VERSIONING.md's "Owed
  before 3.0.0" list already carries after #536.

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.

2 participants