Skip to content

Codec facts check

rebaserHEAD edited this page Oct 18, 2026 · 1 revision

reference, committed verified 2026-10-18 bootstrap c3409ba run 2026-10-18

The client facts table and the check that holds every declaration to it: the table's format, how two layouts are compared, how a disagreement is pinned, and where the check stands. Why the codec is checked this way is on Codec.

The client facts table

Rows

One JSON Lines row per wire opcode, 1,119 rows: 366 client to server, 718 server to client and 35 both ways. A row holds the direction, and for each side whether the layout is fixed or computed, its total bits on the empty path, its field widths in order, where its computed parts (strings, arrays, unions, blobs) sit, and the client's full layout in the layout tree notation. The widths are taken from the tree, so where the client copies raw bytes they're bytes:

{"op":154,"dir":"c2s","c2s":{"kind":"fixed","bits":81,"widths":[32,16,32,1],"tree":"32 16 32 1"}}

The summary

wire-facts-16042.meta.json records the format, the build and the counts:

Key Value
schema 2
build 16042
rows 1119
sides_without_tree 3: the client envelopes ClientEnvelope (0x0244), NestedMessage (0x025C) and TaggedMessage (0x038C), checked on the empty path

The table is our own numbers, public beside the codec, so public CI checks it with no client data. It holds for both the 64-bit and the 32-bit client of build 16042, whose trees agree on 1,145 of the 1,154 sides. Six, all envelopes, can't be compared between the two, and three differ: in each, the 32-bit client also accepts two selector values far outside the range, and in 0x098B one arm differs as well, settled when its area is read.

The comparison

Same bits on every path

Every declared message's full tree is compared with the client's, node by node. Two layouts agree when they put the same bits on every path. Three pairs of forms are treated as equal before comparing, because the wire can't tell them apart:

Form Equal to
An array counted by a 1-bit field A union of nothing or one element
Consecutive arrays sharing one count and holding fields of one width A single array of their elements together
Fields or strings every arm of a union ends with The same reads after the union

A run of fields split differently agrees if the totals meet, nothing later reads those fields as a count or a selector, and the run crosses no string, array, union or blob: one 14-bit field against an 8-bit and a 6-bit, for example. The same widths in another order, such as 8 then 16 against 16 then 8, are still reported.

The split gate

A message at Fields may not split a value the client reads whole. The check fails a verified declaration that draws one client field as several unless the split is listed as settled, and the list starts empty. One declared field over several the client reads doesn't trip it. It can't see a split past the first node of a sequence that doesn't line up, which is a reported disagreement anyway, on a side with no client tree, or in an arm only one side has.

What's compared, and what isn't

  • For a selector wider than 8 bits, the client facts hold its arms only at 0, 1, 2, 3, 0xFF, 0x100, its top bit alone and all ones, so other values aren't compared.
  • A layout that contains itself is reported, not expanded.
  • It catches wrong widths, missing or extra fields, wrong enum widths, opcodes swapped between different layouts, fields swapped when their widths differ, and anywhere in a full layout: an array's element, a missing or extra union arm, a string of the wrong kind, a count or selector read from the wrong field.
  • It can't see swaps of equal width, signedness, or meaning. Generated round-trip tests cover every declared message on top of it.

Pins

How a pin works

Each disagreement is named by opcode, side and path, such as 0x01C3 client to server [4].element[3], and pinned with its reason. The test fails on a disagreement that isn't pinned, and on a pinned one that no longer occurs, so the list only shrinks when a fix lands. Each pin records the exact problem it covers, and the test fails when that problem's text changes, so a pin can't quietly come to cover a different fault in the same place. Several problems at one place are kept together on one pin.

Where it stands

Each run reports the check on its own lines. In the CI run in the header badge:

Line Reads
The facts check "1119 declared messages: 1087 agree with the client facts; 43 disagreements pinned."
The split gate "53 messages at fields; 0 splits settled."

Most pins read "the client's full layout differs; settled when its area is read", because the message audit redeclares each message as its feature area is checked.

Known edges

  • The three client envelopes are checked on the empty path only, since the table holds no tree for them.
  • Selectors wider than 8 bits are compared at their sampled values only.

Where the code lives

Piece Path
The client facts table and its summary Source/NexusTomorrow.Network.Codec/Facts/
The facts check, the comparison and the pins Tests/NexusTomorrow.Network.Codec.Tests/Facts/
The split gate and its settled list Tests/NexusTomorrow.Network.Codec.Tests/Facts/FieldsSplits.cs

Clone this wiki locally