tables: JSON map key identity for padded and decimal spellings, and the prefix control's blade (#614) - #625
Merged
Merged
Conversation
The three identity corruptions #614 files, as rows on the two gates that already hold §16.2's map key rule: the C++ reference's map gate and the tool's own. All three are red before the repair. FAIL test/tables/maps_main.cpp:1088: !EdgeRowFromJson( eb, t, ... ) FAIL test/tables/maps_main.cpp:1108: eb.GetRoot()->ids.count = 1, want 2 FAIL test/tables/maps_main.cpp:1128: r.kind_mismatch = 1, want 0 maps_test.go:193: a padded key is not a JSON number: the read must stop, got {KindMismatch:0 Clamped:0 Duplicate:1 Malformed:false} maps_test.go:234: expected 2 entries, got 1 maps_test.go:257: the decimal spelling of UINT64_MAX is a key the kind holds: {KindMismatch:1 Duplicate:0} The padded key parts the two engines, which is the sharpest reading of the defect: the tool's digit path takes the space as a byte and lands the key 2402, colliding with the literal 2402 beside it, while the reference's saturates and drops the entry as kind_mismatch. Neither is the page's answer, and one rule cannot produce two. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ing alone (#614) Both engines, one rule each, at the point the defect entered. THE KEY IS THE SPELLING AND NOTHING AROUND IT. Both key readers walked the token with the walk that skips whitespace and comments, which is right BETWEEN tokens and wrong INSIDE one: the walk consumed the padding, the length check then passed, and the digit path was handed the padded bytes. The two engines did not even agree on the wreckage -- the tool's byte arithmetic made " 2" the key 2402, colliding with the literal 2402, and the reference's saturated and dropped the entry. The walk now begins at the token's first byte, so a padded spelling is not a JSON number at all and is malformed on the terms "1-2" is. NOTHING ON THE PATH IS A DOUBLE. The one interpretation read a token with a fraction or an exponent through strtod/ParseFloat, so a 53-bit mantissa decided the identity of a 64-bit key: 9007199254740993.0 rounded onto its neighbour and merged with it, and 18446744073709551615.0 rounded UP to 2^64 and was dropped as outside a domain it sits exactly inside. The token's digits are now read where they stand with the decimal exponent moving the point -- the normalization the wide kinds already use (ParseWide, TableJsonReadWide), over one 64-bit lane -- so a zero fraction is the integer it spells at every magnitude the kind holds. `finite` goes with the double that produced it. No exact reader has an infinity, so the flag could only ever be true; a field's magnitude past sixty-four bits saturates and CLAMPS, which is the page's rule for it (§16.2 Bounds, and the wide kinds' "saturates ... as an int64 field saturates at INT64_MAX"). That moves one hand-authored row: 1e309 in a uint8 was kind_mismatch through the double's infinity and is now a clamp to 255. The row it was standing in wanted a value the walk REFUSES, so it takes a fraction instead, and the corrected rule gets a row of its own beside the float field's, where an infinity IS a kind_mismatch and stays one. The goldens: 37 emitted .cpp files, every one a translation unit carrying the JSON walk, and the diff in each is these lines. No header, no wire, no id, no dump. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…clamped count (#614) Two of the control's four branches spelled `return 0` -- success, "red" -- over a run that observed no prefix at all. A sabotage that broke the read outright, or moved the ledger as well as the bytes, walked out through one of them and the control reported the defect it had not reached. Both now fail, and each says which of the three outcomes it saw. Quoted against a walker sabotaged to refuse the read, and against one whose clamp counter is wrong as well as its bytes: ---- readfail / old control ---- clamp prefix negative control: the sabotaged walker would not read at all: red exit=0 ---- readfail / new control ---- FAIL clamp prefix negative control: the walker would not read this text at all, so the clamp was never reached and the stored bytes say nothing. exit=1 ---- countfail / old control ---- clamp prefix negative control: the clamp went uncounted as well: red, but for the wrong reason exit=0 ---- countfail / new control ---- FAIL clamp prefix negative control: the read counted 3 clamps where the text spells exactly one ... exit=1 And the target now runs the control BOTH ways. A binary that only ever meets the sabotage cannot show it has a blade, so the honest walker is the second arm and the control must find nothing to see there: ./build/schema_test_json_clamp_prefix_negative clamp prefix negative control: the scan resumed past the code point that did not fit, storing 16 bytes the text never spelled in that order: red, as required build/schema_test_json_clamp_prefix_honest FAIL clamp prefix negative control: a walker whose scan resumes after an over-long code point still stored the prefix. exit=1 negative control: the clamp prefix control sees the defect under the sabotage and nothing without it Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The exact reader landed on EVERY integer field, and a reference must not part from its ports on a general path without a ruling. Measured on tabledemo RootConfig: 1e309 in a uint8 was 255 with clamped=2 in the reference and the tool where the C, Go and Rust readers answer 0 with kind_mismatch=1, and 9007199254740993.0 in a uint64 was ...993 against the ports' ...992. That is a change to four readers, not to one walk, and it is not what #614 asks for. So the two interpretations are two functions. TableJsonInterpretExact and interpretIntegerExact read the token's digits where they stand and carry the map key path, which is where a spelling is an IDENTITY and two tokens a 53-bit mantissa cannot tell apart are two keys. TableJsonInterpret and interpretInteger go back to the double, which is the interpretation the ports share, and the field path reads through them again. The five-token table on tabledemo RootConfig is byte-for-byte main's again, in the reference and in the tool: 1e309 in a uint8 and -1e309 in an int64 are kind_mismatch with nothing stored, 9007199254740993.0 in a uint64 is 9007199254740992, the decimal spelling of UINT64_MAX is itself, 2.5 in a uint8 is kind_mismatch, and 1e400 in a float64 is kind_mismatch. The key rows are unmoved: the map gate is green in both engines, and a padded key is still malformed, two keys 2^53 apart are still two keys, and the decimal spelling of UINT64_MAX is still a key. TestRejectedRepeatKeepsTheFirstValue takes its original token back, since 1e309 in a uint8 refuses the repeat again, and the row beside it now pins the field path against the ports rather than against the exact reader. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…assert (#614) #609's retained decision gets a row in both engines: an integer key past the scan's 255-byte buffer drops as kind_mismatch, and the row is built so length alone cannot carry it. The key is 256 bytes and spells the integer 1, which the kind holds, and the prefix that fits the buffer spells 1 as well, so a read that truncated would merge it into the entry beside it and call two keys one. At 255 bytes the same text reads the key and counts a duplicate, which is what makes the row a reading of the bound rather than of the length. The clamp prefix control's header and its Makefile note now state what the control asserts rather than what it used to spell, in the present tense the house wants. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rowan-claude
added a commit
that referenced
this pull request
Sep 7, 2026
NOT THIS ISSUE'S CHANGE, and it is here because the merge carries it. main is red at 19a2372 on its own CI run 34081461383, and it was green at bb5f3bd. #625 moved `internal/codegen/cpptable/json.go` and re-pinned the map goldens it knew about, DepthTable.cpp, FleetTable.cpp and RowsTable.cpp, each by the same 91 lines. Text.schema and its golden landed in #626, which merged after #625's branch was cut, so maps/TextTable.cpp never got the re-pin and `make tables-block-zero-cost` reports it moved. The gap is exactly the 91 lines the three siblings took, the TableJsonInterpretExact reader and the decimal band beside it, which every table holding a map now emits. The file here is the generator's own output, copied by the path `make update-goldens` copies it, with no hand edit: copying the whole maps directory moves this one file and nothing else, which is what says the other three were already current. block zero-cost gate: 105 Table sources byte-identical to their pins Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Sep 7, 2026
make test refuses a missing pinned toolchain by name, and names a skip that is asked for (#599)
#660
Merged
rowan-claude
added a commit
that referenced
this pull request
Sep 7, 2026
…ero-cost gate on main) (#664) #625 moved the emitted JSON walker in every map-bearing unit and regenerated the goldens on its branch before #626's TextTable golden existed on it; the merge of main carried the golden in unchanged, so main's committed TextTable.cpp lags the compiler by the key-reader change and the block zero-cost gate refuses main. Regenerated with main's compiler; no header, wire pin or id moves. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
rowan-claude
added a commit
that referenced
this pull request
Sep 7, 2026
…ure (#605) (#657) * red first: the enum-bound corpus, ten shapes and three controls (#605) SPEC-TABLES.md §2.4 and §11 state the refusal on the bound's PROVENANCE: a positional array whose bound folds from an enum is refused in a TABLE BODY and in a UNION ARM, in every spelling the bound has. The checker follows the SPELLING, so eight of the ten shapes compile clean today. test/tables/enumbound holds one unit a shape. Against this commit: ArmConstCount.schema COMPILES (exit 0) ArmConstMax.schema COMPILES (exit 0) ArmCount.schema COMPILES (exit 0) ArmFolded.schema COMPILES (exit 0) ArmMax.schema REFUSED, naming neither the arm nor the table BodyConstCount.schema COMPILES (exit 0) BodyConstMax.schema COMPILES (exit 0) BodyCount.schema COMPILES (exit 0) BodyFolded.schema COMPILES (exit 0) BodyMax.schema REFUSED, naming neither the table ControlPacket.schema COMPILES (exit 0) ControlPlain.schema COMPILES (exit 0) ControlTypeHeld.schema COMPILES (exit 0) TestEnumBoundProvenanceCorpus reads the directory and holds each file to its answer, so a shape is red the moment it compiles or the diagnostic stops naming the field, the enum, the constant the field spells and `[E]T` as the fix. All ten refused rows are red at this commit and the three controls are green. The controls hold the other edge: the packet wire, a bound that folds from no enum, and the `type`-held case schema#606 rules on, which this issue does not decide and this corpus does not move. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * check: the enum-bound refusal follows the bound's provenance (#605) The rule docs/SPEC-TABLES.md §2.4 and §11 state reads the bound's PROVENANCE and the checker read its SPELLING, so `[E.Count]T` and `[N]T` under a `const N` that folds from either compiled in a table body and in a union arm and carried the positional class §4.1 counts as closed. `enumBoundProvenance` follows a bound to the enum it folds from, through named constants and constant arithmetic at any depth (SPEC.md §4.2), carrying its own visiting guard over the constant graph. It looks at an ENUM alone: a `flags` bound is refused by its own rule, and a bound that reaches neither enum nor flags is a plain positional array. `checkPositionalEnumBoundInClosure` runs once the closure is known, which is what lets a union arm's diagnostic name the table that reaches the union, as #572's closure refusal names the edge that pulled a `type` in. THE WALK IS THE FENCE: it starts at TABLE bodies and descends UNIONS alone, so the `type` a table closure reaches is never visited and schema#606 keeps its ruling, and a `type` no table reaches is not in the closure at all, so the packet wire is untouched. The resolve-time spelling check is gone; the bound still evaluates there, because a bound that cannot be evaluated is a different diagnostic and belongs at the field. `exprSpelling` now renders constant arithmetic, so a bound like `[Grade.Max + 1]` is quoted back as the source spells it instead of as the placeholder `N`. test/tables/V1.schema and V2.schema: the refusal caught two live fields. `tally` in both generations and `ledger` in V1 were sized `[Grade.Max + 1]`, a positional array in a table body whose bound folds from an enum, which is the shape the page says is refused and the fixture rested on the gap. Both keep their extents, 3 in V1 and 4 in V2, spelled as the plain constants TallySlots and LedgerSlots, so every generation the evolution test exercises is unmoved: the growing bound, the clamped count, and V1-positional against V2-keyed for `ledger`. `schema fmt` re-canonicalized the attribute column in both files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * check: the enum-bound gate and its negative control (#605) `check-enum-bound-negative-control` runs the compiler over test/tables/enumbound and reads three answers back. THE GATE: each of the ten refused shapes must be refused, and each diagnostic must name the field or the arm, the enum, the constant where the bound reaches the enum through one, and `[E]T` as the fix. The arm rows must name the table that reaches the union. The THREE POSITIVE CONTROLS must compile: the packet wire, whose `[E.Max]T` is a plain array the connect gate covers; a bound that folds from no enum; and the `type`-held case, which is schema#606's ruling and not this refusal's. THE NEGATIVE CONTROL is the one §2.4 names: it removes the CONSTANT FOLD from the bound check through `go build -overlay`, writing no tracked file, and every row whose bound reaches its enum through a constant must then compile clean. It is targeted rather than blanket, so the four direct spellings must stay refused under the same sabotage. A control that turned the whole rule off would go red for a reason that says nothing about the fold. Both halves are green: gate: ten shapes refused on the bound's provenance, three controls compile negative control: without the constant fold, six folded rows compile clean and the four direct spellings stay refused Wired into `make test` beside check-zero-range-negative-control. It compiles no C++ and needs no toolchain beyond Go. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: the enum-bound refusal's status lines, on the tree (#605) SPEC-TABLES.md §2.4's CHECKER STATUS paragraph stated a split between the rule and the checker, and the paragraph carried its own instruction to be deleted by the PR that closes it. It now states one thing: the refusal reads the bound's provenance and not its text, in a table body and in a union arm, and it names what the diagnostic names. §11's checker-status line says the same in its own register. Both RULING STATUS lines for schema#606 stay exactly as they were: the type-held case is the owner's and this refusal does not reach it. VERSIONING.md: the #540 row leaves "Owed before 3.0.0", whose contract is a claim the page makes with the repository not yet behind it. The repository is behind it. The evolution table's "a keyed array made positional" row named the table body alone, which #605 flagged; it now names the union arm as well and states the provenance rule, which is the whole of what the checker refuses there. USAGE.md carried the same split as an italic caveat, and a caveat that is no longer true is worse than one out of scope, so it is replaced by what the diagnostic actually says. The #606 caveat beside it stays. Present tense, no history, no em dashes added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * goldens: re-pin maps/TextTable.cpp, which main left behind (#625, #626) NOT THIS ISSUE'S CHANGE, and it is here because the merge carries it. main is red at 19a2372 on its own CI run 34081461383, and it was green at bb5f3bd. #625 moved `internal/codegen/cpptable/json.go` and re-pinned the map goldens it knew about, DepthTable.cpp, FleetTable.cpp and RowsTable.cpp, each by the same 91 lines. Text.schema and its golden landed in #626, which merged after #625's branch was cut, so maps/TextTable.cpp never got the re-pin and `make tables-block-zero-cost` reports it moved. The gap is exactly the 91 lines the three siblings took, the TableJsonInterpretExact reader and the decimal band beside it, which every table holding a map now emits. The file here is the generator's own output, copied by the path `make update-goldens` copies it, with no hand edit: copying the whole maps directory moves this one file and nothing else, which is what says the other three were already current. block zero-cost gate: 105 Table sources byte-identical to their pins Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * corpus: drop an em dash from the enum-bound README (#605) House style takes none in anything added here, and the heading carried one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- 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 #614.
Stella's three probes from the #607 re-run, red first in both engines, then one
rule each at the point the defect entered; and the prefix control repaired so
the branches it walks out through establish the defect it names.
What lands
The key is the spelling and nothing around it. Both key readers walked the
token with the walk that skips whitespace and comments. That is right BETWEEN
tokens and wrong INSIDE one: the walk consumed the padding, the "did the number
end at the token's end" check then passed, and the digit path was handed the
padded bytes. The walk now begins at the token's first byte, so a padded
spelling is not a JSON number at all and is
malformedon the terms §16.2 gives1-2, with the read stopping where §16.1 stops it.Nothing on a KEY's path is a double. The one checked interpretation (#607's
C7) read a token carrying a fraction or an exponent through
strtod/ParseFloat, so a 53-bit mantissa decided the identity of a 64-bit key. A key'sdigits are now read where they stand with the decimal exponent moving the point
-- the same normalization the wide kinds already use,
ParseWideandTableJsonReadWide, over one 64-bit lane. A zero fraction is the integer itspells at every magnitude the kind holds.
The exact reader is the KEY path's and no other's. It lives in
TableJsonInterpretExact/interpretIntegerExact, beside the interpretationthe FIELD path keeps:
TableJsonInterpret/interpretInteger, unchanged, withthe double and with
finite. That split is the whole of the decision insilence 2 below. A key is an IDENTITY, so two spellings a mantissa cannot tell
apart are two keys and the reference may rule alone; a field's value is a
quantity under a clamp, read the same way by the C, Go and Rust ports over the
same texts, and moving it moves four readers at once.
Engines:
internal/codegen/cpptable/json.go(TableJsonMapKeyValue,TableJsonInterpretExact) andinternal/tabletext/read.go(placeMapKey,interpretIntegerExact). No other backend carries a map key in JSON.Red first
The gates are the two that already hold §16.2's map key rule: the C++
reference's map gate and the tool's own. Commit a9137b9 is the probes alone.
The padded key parted the two engines, which is the sharpest reading of the
defect: the tool's byte arithmetic made
" 2"the key 2402, colliding withthe literal
2402beside it, while the reference's saturated and dropped theentry as
kind_mismatch. Neither is the page's answer, and one rule cannotproduce two.
Green after, both engines, on the same rows.
The FIELD path does not move, and here is the measurement
The exact reader reached every integer field in an earlier revision of this
branch, which parted the reference from the ports on a general path. It is now
the key path's alone, and the field path is byte-for-byte what
origin/mainproduces. Measured on
tabledemo RootConfigthrough the C++ reference andthrough the tool,
maincompiled into a scratch tree for the baseline:1e309inbadgeuint8kind_mismatch=1kind_mismatch=1-1e309intimestampint64kind_mismatch=1kind_mismatch=19007199254740993.0inepochuint6418446744073709551615inepochuint64clamped=0clamped=018446744073709551615.0inepochuint64clamped=1clamped=12.5inbadgeuint8kind_mismatch=1kind_mismatch=11e400inprecisionfloat64kind_mismatch=1kind_mismatch=1Every cell equals main's, in both engines, and the four rows the earlier
revision moved (
1e309to 255 withclamped=2,-1e309to INT64_MIN withclamped=2, the 2^53 spelling to ...993, the decimal UINT64_MAX toclamped=0)are back where the ports have them.
TestRejectedRepeatKeepsTheFirstValuetakesits original
1e309token back with them, andTestAnIntegerFieldReadsItsTokenThroughTheFloatbeside it pins four of thetable's rows against the ports, so a future re-unification has to go red first.
schema packsays the same thing at the CLI. A one-profile tree whose0-ace.jsonis{ "badge": 1e309, "epoch": 9007199254740993.0 }, packed by thisbranch and by main's compiler:
Identical from both, and with
--toleratethe 63 wire bytes compare equal.The KEY rows are unmoved by the restoration: the map gate is green in both
engines, a padded key is still
malformed, two keys 2^53 apart are still twokeys, and the decimal spelling of UINT64_MAX is still a key.
The controls
tables-json-clamp-prefix-negative-controlhad two of four branches spellingreturn 0-- success, "red" -- over a run that observed no prefix at all. Bothnow fail. Quoted against a walker sabotaged to refuse the read, and against one
whose clamp counter is wrong as well as its bytes:
The target now runs the control BOTH ways, because a binary that only ever meets
the sabotage cannot show it has a blade. Under the sabotage it must see the
wrong bytes; against the honest walker it must find nothing to see:
The fifteen
tables-maps-*-negative-controlsabotages are unchanged and staygreen,
keydomainandkeyidentityincluded -- the two nearest this diff.The page, and what it left to be decided
§16.2 settles the map key rule in one clause: an integer key "is its decimal
spelling, quoted, read by the integer rule above and by nothing else, so a token
that rule calls
malformedmakes the key malformed and one with a fractionalvalue or out of the key kind's range drops its entry as
kind_mismatch". Threethings the page does not spell in those words, decided here on reduce-cases and
listed for the owner:
Whitespace inside a key. The page never says the word. The integer rule
says "the token grammar is RFC 8259's", and RFC 8259's number production has
no whitespace in it, so a padded spelling is "a token that is not a JSON
number at all" and the page's own answer for that is
malformed. Decided:malformed, the read stops. The alternative -- trim and accept -- gives one
entry two names, which is the identity corruption the clause exists to
prevent.
Whether a FIELD reads its token the way a KEY now does. Not decided here,
and unchanged by this PR. The exact reader is the key path's alone. On the
field path the reference and the tool still read a fractional or exponent
spelling through the double, exactly as
ctable/json.go:1349-1382,gotable/json.go:1392-1430andrusttable/runtime.go:2150-2190do, so9007199254740993.0in auint64lands ...992 in all four and1e309in auint8iskind_mismatchin all four. That is the reference agreeing withits ports, not a defect this PR leaves standing: the reading is one the
OWNER settles for all nine at once, and it is put to him in Owner ruling wanted: exact integer interpretation for integer FIELDS in the text form, across the nine (1e309, decimal spellings at 2^53, decimal UINT64_MAX) #629 with the
measured table. Under either answer there the nine move together, and this
PR moves none of them.
Where these rows live. The task suggested
json-hostilerows. Themapdemounit is not a conformance unit -- no unit in MANIFEST.txt declaresa map -- so the probes went to the two JSON walk gates that already hold
§16.2's key rule (
test/tables/maps_main.cpp,test/conformance/harness/maps_test.go), which is where tables: the C++ JSON walker's map keys, placement failures and one numeric interpretation (#566) #607 put the rowsthese extend. Admitting
mapdemoto the conformance corpus is a larger,separate move and is the owner's to want.
#609's retained decision is untouched and now PINNED in both engines rather than
held by code alone: an integer key past the scan's 255-byte buffer drops as
kind_mismatch, and the row is built so length alone cannot carry it. The key is256 bytes and spells the integer
1, a value the kind holds, and the prefix thatfits the buffer spells
1as well -- so a read that truncated would merge itinto the entry beside it and call two keys one. At 255 bytes the same text reads
the key and counts a
duplicate, which is what makes the row a reading of thebound and not of the length.
EdgeRow.names' identity row is green beside it.The goldens
33 files, one class of change: 32
*Table.cppundertestdata/goldenandgenerated/bench/tables/cpp/BenchTableTable.cpp, which is the same walk in thetables bench corpus. Every one is a translation unit carrying the JSON walk
(§13.5 puts the walk in the .cpp, which is why no header moved), and the diff in
each is these lines and nothing else. It has exactly two shapes:
The eight are the map-bearing units (
tables/maps,tables/lists). No wiregolden, no id, no dump, no header moved.
bench/LOCKrecords the bench corpus asunder no locked prefix; the prefix list is empty (lifted 2026-09-05) and the
standing gate names the C/C++ PACKET emitters, which this diff does not touch.
origin/main, merged
origin/mainmoved twice under this branch and both are merged in, neverrebased: a0f32ed (#624, #615) and 5e04d3f (#627, docs).
make update-goldensafter each merge moved nothing, so the merge result and the regenerated tree
agree byte for byte, and no golden was touched by hand.
make test, on the bench
make testwhole, on this Mac Studio, on the merged head. It stops atbuild-conformance-cswith/bin/sh: dotnet: command not found-- the absenttoolchain, not a result.
go test ./...is clean.Toolchain absences on this bench (#599): no node, dart, java, elixir or dotnet,
so the JS, Dart, Java and Elixir legs skip silently and the C# legs stop. CI has
all of them, and its twenty checks are green on 6f6be17.
Twenty
FAILEDlines stand in the log before the stop and every one is anegative control reporting its own red, each sitting beside its
negative control: ... turns ... REDline. No non-toolchain failure anywherein the run. The gates this PR touches, green in it:
The four targets named on this work were then run on their own, past the stop,
and are green:
tables-maps,tables-json-map-walk,tables-json-clamp-prefix-negative-control, andtables-maps-negative-controls, whose fifteen sabotages each turn the map gatered as required,
keyidentityandkeydomainamong them.