docs: the tutorial, zero to every feature (#447) - #520
Merged
Conversation
gafferongames
force-pushed
the
tutorial
branch
from
September 4, 2026 14:59
511e96b to
9ae2715
Compare
Contributor
Author
|
Rulings from the cold read, under the owner's word of 2026-09-04:
Structural fixes from the read land on this branch before it leaves draft: one cumulative unit with every name declared where introduced; the C++-only table forms in a second unit named as such in part 7; the sibling checkouts stated with tags; every program and file shown before it is used; the foreign-unit outputs replaced. |
gafferongames
force-pushed
the
tutorial
branch
from
September 4, 2026 15:42
0876f92 to
b2f557d
Compare
…nts (#447) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ers (#447) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rmat (#447) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
#447) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ng the compiler (#447) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…gs (#447) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
) Every command and program in parts 1 through 5 re-run against bin/schema built from b74a7d4. The packet wire is untouched by the id-table wire: both protocol ids, every generated header excerpt, every diagnostic and every program output are byte-identical to what the page already carried. Only the build stamp in part 1's install block moved. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Re-run against bin/schema built from b74a7d4 (#507). What moved: a saved ShipConfig is 89 bytes where it was 42, an all-default one is 10 where it was 2, the report carries a sixth member (the refusal verdict), and a truncated file now yields nothing at all rather than a good prefix — the id table is the trailer, located from the end, so a cut file has no vocabulary to name its fields with. The page now teaches the form byte and the refusal verdict beside the five counters. What did not move: every drift result and every counter value, in both directions, and all nine targets still generate the surface. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Re-run against b74a7d4 (#507). Every Measure moved: the fighter is 10 and 22 where it was 2 and 11, GameConfig is 142 where it was 99, and the three tool messages are 79, 49 and 45 where they were 42, 22 and 18. What did not move: the optional storage layout, all four ? refusals word for word, the None-key abort in a release build, the keyed array's survival of a mid-enum insertion with unknown=0, and the message union's tag enum and arms. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Re-run against b74a7d4 (#507). What moved: the route wire is 163 bytes where it was 172, the fleet is 145 where it was 165, and the four-thousand-node parallel build is 51,904 where it was 111,989 — the id table in the trailer carries each field id once, so a pointer-heavy graph is where the new form is cheapest. The page now says so beside the number. What did not move: the region size, sharing and null preserved through the round trip, the builder's 8,264 bytes, the map's ascending-key iteration and Find, and all three pointer refusals. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…#447) Re-run against b74a7d4 (#507). Nothing in part 9 moved: the JSON a table writes and reads is text, and the wire's framing does not reach it. Every document, every report line, the &node graph form with sharing preserved, the unpacked tree's file list, --one-file, and the strict-mode refusal are all byte-identical to what the page carries. The packed file is larger for the reason part 6 now explains, and the page never printed its size. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Re-run against b74a7d4 (#507). What moved: the baseline projection is version 7 where it was 6, and its field ids are the wire's new 64-bit hashes where they were 16-bit. What did not move: every one of the three verdicts word for word, both was guard rails, the --update refusal without a reason, the history block the override appends, and the was round trip reading a v1 file into the renamed field with unknown=0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Re-run against b74a7d4 (#507). What moved: both build versions, and the facts projection is form 3 with 64-bit field ids where it was form 2 with 16-bit ones. What did not move: the cook's own sizes — 408 bytes, 328 data, 16 attribution — because a cook is a laid-out region and not the wire, so the id-table form does not reach it. Nor did Open, cook-check, the uncook byte-for-byte proof, the big-endian refusal on a little-endian build, the target-neutral build version across byte orders, or the protocol id's independence from a table edit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Re-run against b74a7d4 (#507). What moved: the build version stamped in the block prologue, in both the C++ and the C halves. What did not move: the block is 56,064 bytes of a 196,672 maximum, exactly as before — the block form is a laid-out extent and the id-table wire does not reach it — and the count refusal, the C read through the aligned base, and the same-build refusal after a one-sided rebuild are all unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Re-run against b74a7d4 (#507). The packet-wire handoff is unchanged: C reads the C++ packet bit for bit. The table-wire handoff is NOT, and the page now says so rather than claiming a crossing that does not happen today. C++ reads its own 89-byte save cleanly and both C and Go answer malformed on the same bytes, because the reference is on the new form and the eight ports have not moved to it. The page shows all three outputs, names what the ports do with bytes they cannot read (report malformed, misdecode nothing), notes that only C++ carries the refusal verdict, and points a reader who needs another language today at the packet wire, the cook or the block. Filed as G-15 in #521. Also moved: the reflection walk's ids are 64-bit and the enum field's kind is 30 where it was 7. The embedding demo is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One cumulative unit named starlight, and the prerequisites a stranger needs before parts 3 and 13: the Go toolchain, the clone, make, the PATH step, and the two sibling serialize checkouts at the tags this build pins (v1.16.2 and v1.9.2). Every diagnostic now shows its Bad.schema before its output, with the error trailer. New: a paragraph saying once that every id and byte count on the page belongs to the unit as it stands at that point, and naming schema id and schema projection as the way to compare. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Enums and flags land in the cumulative starlight unit, with the whole file shown after the edit. Pending is declared where it is discussed. Every diagnostic shows its Bad.schema and its error trailer. SystemFlagsNamesMax is shown as generated rather than asserted, the ADL note now points at the program's using-directive, and the program is shown entire and runs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ShipState joins the cumulative unit with the constant it needs shown in place. The program is shown entire, with the forged-packet half in it rather than as a loose fragment, and the compile line names -I gen -I ../serialize, the checkout part 1 now makes. The five range refusals show the Bad.schema shape they share and each carries its error trailer. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Vector2/GameVector2 replaces the Vec2/Vec3/GameVec2 muddle: one declaration in its own file of the same unit, one native header shown in full, one field on ShipState, and the program exercises the operator. The retired [<= N] sentence is gone and the [2..8] count hazard is demonstrated with the generated write rather than asserted. Game.schema is shown entire at the end of the part, and the program compiles as shown with -I gen -I . -I ../serialize. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ChatLine, Snapshot and MaxPayloadsPerPacket are declared where the page uses them, in Net.schema of the same unit, and the program builds and reads a real two-payload Packet rather than a loose FireCommand. The scalar-arm refusal is shown here, where a reader first wants it, and it names the table closure part 7 builds. The id demonstration adds an arm and takes it back out, showing the id return. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ruling 1's rule lands here, where tables first appear: the table wire is C++'s within one build, the eight ports are named with their issues #511 to #518, and the packet wire, cook and block cross all nine. The check notice is explained with this unit's own output and its exit status. The evolution demo copies the whole unit to starlight-2.0 and starlight-3.0, so both programs are shown entire and every file they read is created on the page. TableReport is shown and printed with all six members, duplicate included, and the truncation claim is demonstrated at three cut points rather than asserted. The flags law now uses this unit's own SystemFlags. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GunnerSettings, WeaponConfig and MaxWeapons are declared where the page uses them. The second unit lands here as the ruling asks: starlight/tools, its own package, holding the C++-only table forms (a union a table closure holds, with table, scalar and payload-free arms; an array of unions; *string and *bytes), introduced by the refusal that names the move. The main unit is shown still generating for all nine. config.cpp, none.cpp, keyed.cpp and tools.cpp are each shown entire with their compile lines, and config.bin is created here for parts 9 and 11. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Pointers stay in the starlight unit, which still generates for all nine, and maps move to the tools unit where the C++-only forms live. SetName and heads are declared in the programs that use them, and route.cpp, damage.cpp, wide.cpp and fleet.cpp are each shown entire with their compile lines. The 'a forged wire with a reference loop is refused' claim is replaced by what the loader actually does: an exhaustive single-bit sweep of an 80-byte pointered wire, showing 295 stopped, 345 opened, and one that hands back a self-referential node under an all-zero report. The page tells the reader to bound their own walks. Filed as G-19 in #521. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
json.cpp and loadpacked.cpp are shown entire with their compile lines, and the graph half is in the same program rather than a loose fragment. The level_name edit is shown before the load prints it, the Corvette edit is shown as the file the designer saves, and the 'very strong' edit is reverted and repacked on the page before part 11 cooks the tree. Absent optionals not appearing in ToJson is stated where the output shows it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The baseline is created over the real cumulative unit, so the notice part 6 introduced is answered here and check goes silent again on the page. ShipConfig gains a systems SystemFlags field so the flags law is demonstrated on this unit's own flags rather than an invented one. The was diagnostic is shown beside the declaration that produced it, and the was round trip reads ship.bin, written in part 6 under the old field name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
cook.cpp is shown entire with its compile line, and Mine.cook is produced by the program on the page before cmp compares it. The build-version demonstration adds a real field to this unit's GameConfig, shows the baseline passing it, the protocol id holding and the build version moving in one place, and takes it back out. Every cook command runs against the tree part 9 packed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
RenderShip, RenderLaser and RenderFrame join the cumulative unit with the two constants they need, and the unit is shown still generating for all nine. produce.cpp and consume.c are shown entire with their compile lines, frame.block is created by the program on the page before the consumer reads it, and TableBlockRefusal is shown as generated so the field names in the program match. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Ruling 1: the part now shows the three crossings that work — the packet wire, the cook and the block, each from the starlight unit into C, each run — instead of staging a failed table handoff. No rewrite narrative and no in-flight vocabulary in the page's own voice; the C++-only rule is stated once in part 6 where the promise is made. writepacket.cpp, readpacket.c, opencook.c and reflect.cpp are shown entire with their compile lines. The reflection walk now distinguishes an enum's vocabulary from a flags field's by the variant_id tell, so Shields is no longer dropped, and the Go embedding example has its go.mod, its imports and a sorted iteration order with the reason that determinism is a generator's contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The 'silent on success' rule is reconciled with the notice part 6 introduced and part 10 answers, with the rule stated as success is silent except while a table format is unguarded. Every edge refusal shows its Bad.schema and its error trailer, the bare-bytes case shows all four errors it really prints, and the UnitView gap is shown against this unit's own generate listing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
) save2.cpp, load3.cpp, keyed.cpp and j.cpp now appear with their sources and their compile lines, so every ./program the page runs is a program the page built. Verified against the real files: load3.cpp was rewritten in the working tree to match the printf the page shows, and re-run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The cold reader's last item. Only the check branch calls the unguarded-format nudge; generate prints nothing on success, verified: $ schema check . notice: nt declares 1 table and . holds no tables.baseline — ... $ schema generate --lang cpp --out gen . $ Corrected in part 14 and in the identical sentence part 6 carried. Part 10's separate claim, that generate diffs the closure against a committed baseline, stands: generate prints the same warnings and the same refusal as check and exits 1 on a refused edit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
gafferongames
force-pushed
the
tutorial
branch
from
September 4, 2026 16:15
5e444f7 to
c166888
Compare
gafferongames
marked this pull request as ready for review
September 4, 2026 16:15
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.
docs/TUTORIAL.md: fourteen parts, from an empty directory to a program that uses every feature the C++ reference implements. The narrative path through the material USAGE.md presents feature by feature, linked from the README hub above it.Scope, and the commit the outputs come from
Every command, output, id, header excerpt and byte count on this page was produced by
bin/schemabuilt frombafdb69(schema v2.4.0-148-gbafdb69), with the serialize siblings at the tags this build pins:serializev1.16.2 andserialize.cv1.9.2. Every C++ and C program on the page was compiled and run, one at a time.Re-verified unchanged at
64325e1, this branch's base: every protocol id the page prints, the build version, the baseline projection version and Part 6's byte counts all reproduce. #530, #531, #532 and #534 moved the spec and the certification legs, not the emitted bytes.The tutorial's scope is the feature set the C++ reference implements at that commit. A specification page without an implementation is not taught, because a reader who follows the page must be able to run it. Three features are in the spec at
bafdb69and refused by the compiler, so they are parts this page owes their implementation PRs:bafdb69wstring(N), the wide string type (#510)Bad.schema:3:19: expected newline after field, found "("tagsand doc columns (#529)Bad.schema:3:21: unknown attribution "tags" — the vocabulary is typed and closed per compiler version (SPEC §4.2)retain-unknown(#527)Bad.schema:3:9: a table declaration takes no qualification (docs/SPEC-TABLES.md)When each lands, it gets its section, run the same way as everything else.
One unit, cumulative
The page grows one schema unit,
starlight, from four constants to nine files, and every name it uses is declared in the part that introduces it. Every pasted output belongs to that unit as it stands at that point, and Part 1 says so once and namesschema idandschema projectionas the way a reader compares.Game.schemaShipState), 4 (Vector3, the grownShipState)Vectors.schemacpp_nativemapping andgame_vector2.hNet.schemaContactthroughPacketandPacketHeaderConfig.schemaShipConfig), 7 (GunnerSettings,WeaponConfig,GameConfig, optionals, the keyed array), 10 (systems)Scene.schemaWaypointandRouteRender.schemaRenderShip,RenderLaser,RenderFrameA second unit,
starlight/tools, holds the C++-only table forms, introduced in Part 7 by the refusal that prescribes exactly that move (... or move the union and its tables to their own unit). It holds: a union a table closure holds, with a table arm, a scalar arm and a payload-free arm (ToolBody); an array of unions (ToolLog);*stringand*bytes(Attachment,AttachmentSet); and Part 8's maps (Fleet). Because they live there, thestarlightunit generates for all nine targets, which the page shows in Parts 6, 7, 8 and 12, and Parts 12 and 13 run.Ruling 1: present state
The rule now lands in Part 6, where tables first appear and the promise is made: the table wire is C++'s within one build, the other eight targets are named with their issues #511 to #518, and the packet wire, the cook and the block cross all nine.
Part 13 is present state. It runs the three crossings that work, each from the
starlightunit into C: the packet wire (C reads aShipStateC++ wrote), the cook (C opensGame.cookC++ cooked), and the block (Part 12'sproduce.cpptoconsume.c). No failed handoff as a centerpiece, no rewrite narrative, and no in-flight vocabulary in the page's own voice. Where a refusal appears it is quoted as the tool's own text.Prerequisites, and programs that build
Part 1 states the Go requirement, the clone,
make, the PATH step, and both sibling checkouts with their tags, before Part 3 and Part 13 need them.Every
./programthe page runs is a program the page shows, with its compile line, and every file it reads is created on the page first:ship.bin,forged.bin,dart.bin,config.bin,packed.bin,frame.block,packet.bin,Game.cookandMine.cook.Notable corrections from the cold read
Vec2/Vec3/GameVec2muddle is oneVector2inVectors.schemawithgame_vector2.hshown in full and exercised by the program.TableReportis shown and printed with all six members after tables: the id-table wire (#435) #507,duplicateincluded.forged.binthe page writes.checknotice is explained in Part 6 with this unit's output and its exit status, answered in Part 10, and reconciled with "silent on success" in Part 14.[<= N]sentence is gone; the[2..8]count hazard is shown through the generated write instead of asserted.Findings
#521 carries every finding, in #447's six categories, with a reproduction command each.
G-19 is the one worth reading first. A single flipped bit in an 80-byte pointered wire loads with an all-zero report (
malformed=0,refused=0,unknown=0) and hands the caller a node whosenextpoints at itself. The obvious walk over it never terminates. The sweep is on the page and in the issue.Also new in this pass: the table wire not crossing languages (G-15) and the refusal verdict living only in C++ (G-16), both the ports work #507 declared; two refusals still describing the wire as "field-tagged TLV" (G-17); and #447's F-16 re-observed now that the local and UTC dates disagree (G-18).
🤖 Generated with Claude Code