tables: the message form's LoadRetain, and the manifest's retain surface (#525) - #679
Merged
Conversation
The pinned batch and the two rows that read it, before the function exists. RT2 writes a form-2 batch of two bodies over the same values the file rows read: the fields at three depths that are retained, the whole `parcel` table the resolving walk reads, and every class this form can put on the unknown arm — a field of kind 17, an array whose element kind is 17, a table whose payload meets a 17 three bodies down, an unknown enum variant reference, an unknown union arm id and an unknown keyed-array slot. The reference's row and the oracle's read that one vector and require the same counters and the same saved file. Both are RED: test/tables/retain_main.cpp:1023:20: error: no member named 'NodeLoadRetainMessages' in namespace 'tblrt1'; did you mean 'NodeSaveRetainMessages'? internal/tablewire/retain_test.go:716:29: undefined: tablewire.DecodeRetainMessages Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`LoadRetain` reads a form-2 body as it reads a file's, the resolving walk replacing every reference with the id it names AGAINST THE CONNECTION'S VOCABULARY instead of a trailer (§3.3). `SaveRetain` writing form 2 still refuses by name, so the round trip a caller has is form 2 in and form 1 out, and the file carries its own table and takes §6.6 unchanged. THE RECORD IS THE FILE FORM'S OWN, to the byte, so the capture is a TRANSCODE as well as a resolve: a bitpacked value is read at the width its announced shape states and written at the width the file form spells, and from there it is the same record, laid down in the same slots and read back by the same emit walk. Neither engine carries a second copy of the emit side. THE SKIP RUNS FIRST AND THE CAPTURE SECOND, over the same bits. The plain read's verdict on an unknown entry is the SKIP's, whether or not this build retains, and a capture that lands anywhere but the skip's own end drops the record. It is two flat passes over one record's bits and never a walk that doubles at every level. A BATCH TAKES ONE REGION AND ONE RETENTION BUFFER A BODY: each body carries its own node directory inside that one region (§3.3), so a record's first step stays an index into that body's own directory and SaveRetain's accounting is the file form's unchanged. The reference and the oracle read the same pinned batch and write the same two files, 442 and 93 bytes, byte for byte: retained=10 retain_lost=6 unknown=17 `LoadRetainMessages` is claimed in tableGeneratedVerbs and §11 on that list's own rule, because it is emitted: 52 suffixes become 53 and 68 become 69. Goldens: every C++ table header of a unit with a variable-length table gained the retaining message family. Six under testdata/golden/tables/maps were already stale on main — their committed text predates #658's retain runtime and #620's message text rule — and this repin brings them level; the other eighteen moved on this change alone. No WIRE golden moved: retention is additive and the plain codec is untouched to the byte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…#525) MANIFEST.txt gains `retain` and `retain-message` lines and the nine rows §6.6 owes: the round trip at depth, the truncated record, the id list one short, the five excluded classes a wire can carry to the unknown arm, and the message form's tail. FORMAT.md's page and the harness's two surfaces follow: `retain` carries the COUNTERS and `retain-save` the BYTES, so each is one shape. THE TWO CAPACITIES TRAVEL AS RULES AND NOT AS NUMBERS. A record's byte cost is the port's own, so `short` says ONE BYTE SHORT OF THE LAST RECORD and a leg resolves it against its own full load; the id list's capacity is a COUNT and rides as one. The reference answers both surfaces and the oracle claims the same nine rows in the harness's own test, against the manifest's counters and the pinned saves. cpp retain pass 9/9 retain-save pass 3/3 c retain absent retain-save absent go retain absent retain-save absent rust retain absent retain-save absent THE NEGATIVE CONTROL is `tables-message-form-retain-negative-control`: it takes the message path's call into the resolving walk out of the emitter through an overlay, regenerates the RT set and requires the retain gate to go red on the pinned batch's own row and on nothing else. FAIL line 1037: report.retained == 10 TestTheTailIsUnconditional gained a map-entry skip. The vocabulary tail never names a map's generated entry (§2.8, §20.2) and the test's own table set did not skip one, because no connection unit carried a map until RT2 did. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
§6.6's backend status names the reference as carrying `LoadRetainMessages` beside the three verbs, and what is still owed is the eight ports. Its HELD BY TEST list is present tense and says which nine rows are the conformance manifest's own data and why the rest are the two engines' own gates: a record's byte cost is the port's own, an allocation audit is a language's own instrument, and a refusal by name is a compile error rather than an answer a driver writes. §3.3's retention paragraph says both halves are carried, states that the record is the FILE form's own, and states the batch's one buffer a body. §3.3's claimed set names `LoadRetainMessages` beside the three plural verbs. VERSIONING's #525 line: the eight ports only. ROADMAP's retain-unknown cell moves to ✅ for cpp, and 339 cells become 340. Every gate §6.6 names for a backend exists in the reference: the three verbs, the fixed-class refusal by name, the message form's read, the second family of body functions, all sixteen HELD BY TEST rows in test/tables/retain_main.cpp, and the nine conformance rows the harness answers 9/9 and 3/3. No em dash rides in a line this branch adds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`go build ./tools/sabotage` writes ./sabotage beside the tree, and one commit picked it up. The tool is run with `go run` everywhere it is used. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`message-clamp-drops-surplus` and `message-surplus-lands-on-zero` both anchor on the array element loop of the message decoder, which gained the `mine` flag that says whether an element is this build's: a surplus element is decoded into a scratch where a file's reader steps over it by its length, so retention has to be off for it (§6.6). The anchors take the new line and the blades cut what they always cut. The tool caught it, by design: `edit 0 matched 0 times, want exactly 1. The emitter moved away from the anchor, so this negative control no longer breaks what it names.` Both go red on their own rows again, and `make tables-message-form-negative-control` is green end to end. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…er the merge (#525) The message retain codec declared one root, one retention buffer and one id list for the FORM's 256 bound rather than for the batch's own body count, which is a megabyte a body a driver never uses. The batch is opened for its count first, and the arrays are that long. The merge brought three new maps units, Crews, Pairs and Trails, and every one gained the retaining message family, as every unit with a variable-length table does. GateTable, AssetsTable and DocsTable gained a forward declaration each. Every one of the six only GAINED lines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A blank line the em-dash pass left behind. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
golangci's gocritic reads the three-way branch as one, and it is: an element of an unbounded array, an element inside this build's bound, and one past it. `message-surplus-lands-on-zero` anchors on that branch and takes the switch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
# Conflicts: # Makefile
This was referenced Sep 7, 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.
Two of the three things §6.6 still called owed on the C++ column: the MESSAGE
form's
LoadRetain(§3.3) and the conformance manifest's retain surface. Theeight ports are what is left, and VERSIONING's #525 line now says only that.
Red first
The pinned batch and the two rows that read it landed before the function did.
RT2 writes a form-2 batch of two bodies over the same values the file rows read:
the fields at three depths that are retained, the whole
parceltable theresolving walk reads, and every class this form can put on the unknown arm, a
field of kind 17, an array whose element kind is 17, a table whose payload meets
a 17 three bodies down, an unknown enum variant reference, an unknown union arm
id and an unknown keyed-array slot. Both engines were RED (b911247):
The compiler's own suggestion is the shape of the change: the write half was
already there, refusing by name, and the read half was not built.
The function, in both engines
LoadRetainreads a form-2 body as it reads a file's, the resolving walkreplacing every reference with the id it names against the CONNECTION'S
VOCABULARY instead of a trailer.
SaveRetainwriting form 2 still refuses byname, so the round trip a caller has is form 2 in and form 1 out.
The record is the FILE form's own, to the byte, because a retained record
carries the field's bytes with every reference resolved so that re-emitting it
into any id table is correct, and the table it is re-emitted into is a file's.
So the capture is a TRANSCODE as well as a resolve: a bitpacked value is read at
the width its announced shape states and written at the width the file form
spells. From there it is the same record, laid down in the same slots and read
back by the same emit walk. Neither engine carries a second copy of the emit
side; the reference's message capture writes through
TableRetainInSlot,TableRetainInLebandTableRetainInId, and the oracle's writes through thesame
resolverthe file capture fills.The skip runs first and the capture second, over the same bits. The plain
read's verdict on an unknown entry is the SKIP's, whether or not this build
retains, and a capture that lands anywhere but the skip's own end drops the
record. Two flat passes over one record's bits, never a walk that doubles at
every level.
Reference:
<T>LoadRetainMessages,<T>LoadMessageBodyIntoRetain,<T>LoadMessageBodyRetainand<T>NodeMessageBodyRetain, emitted by the retainpass with the path threaded at every child-body descent, the four excluded
classes counted at their own arms and
TableMessageRetainCaptureat the unknownone. Oracle:
DecodeRetainMessagesandinternal/tablewire/retainmessage.go.Both read the pinned batch and write the same two files, 442 and 93 bytes, byte
for byte, at
retained=10 retain_lost=6 unknown=17.The manifest's retain surface
MANIFEST.txtgainsretainandretain-messagelines and the nine rows §6.6owes: the round trip at depth, the truncated record, the id list one short, the
five excluded classes a wire can carry to the unknown arm, and the message
form's tail. The harness gains two surfaces,
retainfor the COUNTERS andretain-savefor the BYTES, split for the reason the block surfaces are.The two capacities travel as RULES and not as numbers. A record's byte cost
is the port's own, so
shortsays ONE BYTE SHORT OF THE LAST RECORD and a legresolves it against its own full load; the id list's capacity is a COUNT and
rides as one.
The eight ports refuse the surface under the harness's own absent-is-not-failure
rule: a port carries no retention at all, so the three verbs are a missing
FEATURE and not a failing test, and §6.6's backend status says so.
The oracle claims the same nine rows in the harness's own test, against the
manifest's counters and the pinned saves.
The controls
make tables-message-form-retain-negative-controltakes the message path's callinto the resolving walk out of the emitter through an overlay, regenerates the
RT set and requires the retain gate to go red on the pinned batch's own row:
It cuts exactly one line: every file-form row of the gate stays green, so the
blade is the message path and nothing else. Green half: the same gate built from
the unsabotaged compiler is
retain: ok, plain and under ASan/UBSan.The silences, listed
say "the message form's
LoadRetain", which is the FILE form's verb, and§11's claimed set carries
SaveRetainMessages, the write refusal, with noread spelling beside it. Reduce-cases:
<T>LoadRetainMessagesbeside<T>LoadMessages, plural because the primitive is a batch. It is EMITTED, so§11's own rule claims it:
tableGeneratedVerbsand the page's fenced blockboth gain it, 52 suffixes become 53 and 68 become 69.
buffer belongs to one loaded region and §3.3 says a batch is ONE region with
M bodies, each carrying its own node directory. Reduce-cases: one retention
buffer PER BODY, an array parallel to the caller's array of roots. It keeps a
record's first step an index into the directory of the body it came from,
which is what §6.6 says a first step is, and it leaves
SaveRetain'saccounting the file form's unchanged. A single pooled buffer would make every
save count the other bodies' records as lost.
it. §4.2 states the arm as "the VARIABLE-CLASS FILE ROOTS and nothing else,
because a fixed-class root's
LoadRetainis refused by name and a form-2SaveRetainrefuses by name". The second reason still stands after thischange: the leg's third requirement compares the bytes a leg SAVES, and there
is no retaining form-2 save to compare. Adding message roots would mean
rewriting that sentence to a cross-form comparison, which is a third thing
the page does not ask for here. The line prints the seeds it leaves out
already. Flagged rather than done.
unknownis 17 on the message row where the two retention counters sum to16. The seventeenth is the plain read's own and predates this change: a bit
stream has to be walked past, so a DISCARDED SLOT's element body is decoded
into a sink where a file's reader steps over it by its length, and the field
inside it this build cannot name counts there. The test asserts the plain
read gives the same 17, which is what makes it the read's and not retention's.
Retention keeps nothing under a discarded slot: the slot is the exclusion and
everything below it went with that one
retain_lost.Two things found in passing
testdata/golden/tables/mapswere already stale onmain. Their committed text predates tables: retain-unknown in the Go oracle, and the wire fuzzer's retention leg (#525) #658's retain runtime (
case 15: case 30:in the capture and the emit) and The message form's content rule and clamp for string(N) are stated in §3.3 and carried by neither engine #620's message text rule. Verified bygenerating from a
bin/schemabuilt at this branch's base:CellsTable.h,ChunksTable.h,DocsTable.h,RunsTable.h,SlotsTable.h,SpansTable.h.This repin brings them level; the other eighteen moved on this change alone,
and every one of the twenty-seven only GAINED lines.
TestTheTailIsUnconditionaldid not skip a map's generated entry. Thevocabulary tail never names one (§2.8, §20.2) and the test's own table set
counted it, which no connection unit had exercised because none carried a map
until RT2 did.
The cell
ROADMAP's retain-unknown cell moves to ✅ for cpp, 339 cells become 340. Every
gate §6.6 names for a backend exists in the reference after this change: the
three verbs on every variable-class root, the refusal by name on every
fixed-class one, the message form's read, the second family of body functions,
every HELD BY TEST row in
test/tables/retain_main.cpp, and the nineconformance rows the harness answers 9/9 and 3/3. The eight port columns stay ❌
and VERSIONING's #525 line says the ports are what remains.
Goldens
make update-goldensmoved twenty-seven C++ table headers, every one of a unitthat declares a variable-length table: they gained the retaining message family
(
LoadMessageBodyRetainper member,NodeMessageBodyRetain,LoadMessageBodyIntoRetainandLoadRetainMessagesper variable root) and themessage-retain runtime beside the file one. Six of them were stale on main as
above. No WIRE golden moved: retention is additive and the plain codec is
untouched to the byte. Three new pins:
retain_message.bin, its two saves, andretain_conn.bin, RT2's announcement, which the manifest's connection linenames.
make testRan to a real red, mine, and fixed:
message-clamp-drops-surplusandmessage-surplus-lands-on-zeroboth anchor on the array element loop of themessage decoder, which gained the
mineflag that says whether an element isthis build's. The tool caught it by design:
Both re-anchored, both red on their own rows again, and
make tables-message-form-negative-controlis green end to end (rc=0). Everymessage sabotage was re-scanned for its anchor after that; all thirty-three
match exactly once.
Green on the way there, in the same run: the whole packet and table suite,
tables-retainplain and under ASan/UBSan, the wire fuzzer at0 divergenceson both the plain and the sanitized leg, and the retention leg:make testcannot reach its end on this machine: node, dart, elixir and dotnetare absent, and the run stops at the C# conformance leg.
SCHEMA_SKIP_LEGSisin #660 and not merged. Everything before that stop is green.
Toolchain
Absent here and untested by me: node, dart, elixir, dotnet. Present and run:
Go 1.27, clang, cargo, java.