tables: wstring(N) on the id-table wire, kind 33 (#522) - #617
Merged
Merged
Conversation
…form The id-table wire's wide text (docs/SPEC-TABLES.md §3, schema#522): kind 33 maps onto wstring(N), the C++ table emitter carries the record's char16_t[N + 1] and its int32 used length in code units, the four codec sites, the arm payload, the cook's two-byte scalars, the §16.2 text row and the skip rule. The §11 closure refusal is deleted with its tests inverted to acceptance. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
internal/tablewire encodes and decodes wide text on both forms, internal/ tabletext transcodes it at the JSON boundary through one shared escape grammar, and internal/tablecook writes, checks and reads the char16_t[N + 1] piece in both byte orders. The message form's half is terminal, which is the one thing that differs from the id-table wire's recovery. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
examples-wide/Caption.schema declares every kind 33 site the wire has — a table's own field, a type a table reaches, a union arm and an element — and test/wide/table_main.cpp states one row per sentence of docs/SPEC-TABLES.md §3 and §4 over them, named from the serialize corpus with -table on them. Each control removes one rule from a copy of the table emitter and names the row that goes red. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The wide unit joins the tables corpus as its own directory, five instances pin the kind at every site it has, and the fuzzer gains the ill-formed wide-text and odd-L passes over them. Its control removes the pairing rule from the emitted runtime and the leg then stores what the oracle refuses. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
SPEC-TABLES §11's wstring clause goes with the kind that made it necessary, SPEC.md §4.12's backend status says which wires C++ carries and drops the clause that contradicted the sentence after it, and USAGE, VERSIONING, the FAQ and the roadmap's cell follow. 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>
The wide-text helpers and the shared escape grammar move every C++ Table source, and the wide unit's build-version golden gains kind 33's record layout. NO WIRE GOLDEN MOVED: adding a kind changes no existing byte, which is the closed set doing its job. Wide text's per-target refusal moves ahead of the form refusals, because a target that laid out no member cannot emit the field under any form. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A control that removes a rule must quote the rule's own row rather than a golden mismatch downstream of it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
closureEdge, fieldPos and blockFieldPos existed to name the edge and the line of a wstring refusal that no longer exists, and the closure walk's reachedBy map with them. Removal rides the landing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
gafferongames
marked this pull request as ready for review
September 6, 2026 10:58
…progress (#522) The fixer died with its stream on a network change. Measure before trusting. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
# Conflicts: # Makefile # testdata/golden/build-version/examples-wide.txt # testdata/golden/wide/cpp/WideText.h # testdata/golden/wide/cpp/WideTextWire.h # testdata/golden/wide/id.txt
…ded (#522) The difference check alone is satisfied by the swapped `label_length` and `seq`, so a cook that wrote the units as a run of bytes passed it. All three units of the `char16_t[N + 1]` are now read byte for byte in both orders. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The three that landed while this branch sat all reach package wide: #616's arm-defaults rule moves its protocol id once (0xcd3b46b8e6e1a6d7 to 0x4b9b89c718af5d5e) now that Caption.schema is in the unit, #621 takes the text runtime's lengths as the wire's own 64-bit numbers, and #612's unit registry emits WideView.h and WideView.cpp beside the rest. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…#522) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Sep 6, 2026
rowan-claude
added a commit
that referenced
this pull request
Sep 6, 2026
…goldens (#525) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Closes #522's implementation half.
wstring(N)rides kind 33 on theid-table wire in the C++ reference, the Go oracle and the tool, and
SPEC-TABLES.md §11's closure refusal — the land-and-expand half #510 wrote and
#572 widened — is deleted with its tests inverted to acceptance.
The page half landed as #532/#592 and is law here: nothing below decides
anything the page states, and every silence I had to settle is listed at the
bottom.
What kind 33 is, in the reference
L, a BYTE length, thenL / 2UTF-16 code units two bytes eachlittle-endian (§3). An odd
Lis framing damage on the body that carries it.An unpaired surrogate or a zero code unit is DAMAGE and not data, checked on
the payload AS IT ARRIVES and before the reader's bound: the field reads its
declared default, one
malformedcounts, and the parent reads on pastL. Apayload longer than the bound clamps at a code unit boundary and never
splits a pair — a high surrogate whose low half did not fit is dropped with
it — so no clamp can invent the one thing no wire may put into storage.
Storage is §7.2's row:
char16_t[N + 1]and anint32used length in CODEUNITS, with the terminating zero unit at index
length. The cook writes theunits as two-byte scalars in the cook's byte order, because a record is
written piece by piece and a swap has to know where every scalar begins. The
text form is §16.2's row: the same text transcoded at the boundary, through
ONE escape grammar shared with kind 12 — an unpaired surrogate built in code
writes
U+FFFD, a zero unit writes the\u0000escape (§16.3).Every site, and its control
wstring(N)has four kind-33 sites and they take one rule, because §3 statesit once for the kind: a table's own field, a
typea table reaches, aunion arm, and an element (a type inside an array). Each is declared
once in
examples-wide/Caption.schemaand read once intest/wide/table_main.cpp, whose rows are named from the serialize corpuswith
-tableon them so the two wires' rows line up by name.wstring(N)as a MAP KEY is refused by name (§2.8) and has no site;*wstringis specified ahead of its implementation (§2.5) and no backendemits the blob record, so it has none either. Both refusals are held by their
own test.
Six controls, each quoted red in the report:
wide-table-surrogate-negative-controlwstring-table-refuse-first-high-surrogate-alonewide-table-zero-unit-negative-controlwstring-table-refuse-nul-as-the-only-groupwide-table-clamp-negative-controlwstring-table-clamp-never-splits-a-pairwide-table-odd-length-negative-controlLcheckLrow'sreport.malformedwide-table-byte-length-negative-controlL's doubling on writebuffer[3] == 6tables-wire-fuzz-wide-text-negative-controlThe cook's byte order takes a seventh, run by hand rather than pinned as a
target: with
table_cook_unitsreplaced by amemcpyof the whole run, thegate goes red on
wstring-table-cook-unit-byte-orderand on nothing else. Theolder difference check, that a little and a big cook are not byte-identical,
stayed GREEN under that sabotage — it is satisfied by the swapped
label_lengthandseqalone — which is whycook_layoutnow reads allthree units of the
char16_t[N + 1]byte for byte in both orders.The corpus, the conformance rows and the fuzzer
examples-wide/gainsCaption.schemabeside the renamedWideText.schema(the rename removes a
WideTable.hcollision withtables/examples). Fiveinstances are pinned under
testdata/wire/tables/wide_*.binand named by theconformance manifest as their own
widedemounit — a leg without theconstruct answers ABSENT, exactly as it does for
blobdemo.wide_sitescarries the kind at all four sites at once, which is what gives the wire
fuzzer's new ill-formed wide text and odd
Lpasses four places toplant at (§4.2).
No wire golden moved. Adding a kind changes no existing byte, which is the
closed set doing its job; what moved is the emitted runtime and the JSON
walker, re-pinned.
What the merge with main moved
Three PRs landed while this branch sat, and all three reach package
wide.The protocol id moved once,
0xcd3b46b8e6e1a6d7to0x4b9b89c718af5d5e. That is #616's arm-defaults rule applying to this unitlike every other, and it applies to
Caption.schematoo becauseCaption.schemais now part of the unit — the file this PR adds joined themove rather than causing one.
testdata/golden/wide/id.txtand the announcebytes carry it.
#621 takes the text runtime's lengths as the wire's own 64-bit numbers, which
reaches kind 12's
TableUtf8ValidandTableUtf8Clampin this unit'sgenerated runtime. Kind 33's twin is unchanged and does not need the same
hardening: every kind-33 length passes
room()before it is used, so it isalready bounded by the buffer and non-negative, and
*wstringhas no blobpath to reach the helper by (§2.5).
#612's unit registry emits
WideView.handWideView.cppfor this unit.All of it is regeneration from the merged compiler, proved by
go-test'sgolden comparison rather than asserted.
The measurement
None claimed. The kind is a wire feature and the readers' kind switch gained a
case, so a sitting is owed, but no receipt for one is committed here, and this
bench could not produce a valid window: BENCH-STANDARD §2.5's load preamble
records a one-minute average between 4 and 6 throughout, with three other
lanes compiling beside this one. The numbers an earlier revision of this
description carried have no CSV under
bench/results/behind them, so theyare withdrawn rather than repeated. The sitting is owed before the tables
layer's next release gate, not before this merge.
What the page left silent
and never said whether the units are swapped in a foreign-order cook. The
general rule settles it — a record is written piece by piece and a swap has
to know where every scalar begins — and a
char16_tis a two-byte scalar.Settled on the page here: §7.2's
wstring(N)row now states that eachunit is written at its own two-byte width in the cook's order, never as a
run of bytes, and that the terminator is a unit like any other. The narrow
twin's
char[N + 1]is order-free and gave no precedent.every write in every target", while SPEC-TABLES.md §3 leaves the table
wire's write side unrescoped and kind 12's table
Savecarries no twin of§4.7's write-side check. Settled on the page here: §4.12's sentence is
scoped to the PACKET wire, and the table wire's
Saveis stated to checkthe used length and nothing about the content, which is the shape kind 12
already has there. Ill-formed text is a read-side verdict on that wire.
and neither the C++ message loader nor the Go message decoder carries them
for kind 12 today, a tables: widening on read, the refusal reasons, ill-formed text and comments (#532) #592 gap that predates this PR. Kind 33's half is
implemented here (terminal refusal, a clamp that never splits a pair);
kind 12's twin is owed and untouched. Filed as The message form's content rule and clamp for string(N) are stated in §3.3 and carried by neither engine #620.
map[K]string(N)emitsconst /* ? */ *fromTableEntryFound. Verified against origin/main;wstring(N)as a map value inherits it exactly, and no page sentencerefuses the declaration. Not fixed here: it is kind 12's problem first and
a design question about the entry's
value/value_lengthpair.Filed as A text map value does not compile: map[K]string(N) emits const /* ? */ * in the C++ reference #619.
Las framing damage andnothing else refused" sat two sentences before "The validation above holds
on both". The first clause is deleted as a prose bug; the goldens carry the
rule.
The cell that moves on #366
wstring(N) on the table wire — cpp: ❌ to ✅.
🤖 Generated with Claude Code