The agreed ring vocabulary now lives in two catalogues in eleven locales
each -- gospelInternet.json for the screen legend, packet-strings.json for
the printed sheet -- and nothing held them together. They legitimately
want different SHAPES (the screen renders the axes as standalone labels,
the packet composes them into one string per ring), so neither can import
the other; but the WORDS must match.
The failure that makes this worth a gate is quiet: one cell is provisional
on the Driver's row 89, and it appears in rings 1, 4 and 7. Answer it in
one file and not the other and the sheet and the screen state different
things about the same ring, each correct in isolation, with no error
anywhere. Only a reader holding both would notice.
Measuring first showed 36 of 77 cells already differed, in two classes.
Most were grammar -- the packet's culture terms follow a comma
mid-sentence, so they are lowercase -- but some were genuinely different
words: "Same family" against "Same language family", German "verwandte"
against "Nahe", Hindi "वही जन" against "वही जनसमूह". Those are drift, and
the screen's wording wins because it is what readers already see. The
packet now adopts every term verbatim, lowercasing only the first
character of the culture half, which is a no-op in zh/ar/hi and leaves
German common nouns capitalised.
That adoption caught a bug in my own assertion: I had hardcoded ring 7-9
as "Same family" from CenteredSetTarget.vue's header, while the shipping
string is "Same language family". A summary in a comment is a description
of the data, never a second source of it.
The gate also refuses to pass when it compared nothing -- rename a key on
either side and every lookup returns undefined, every pair is skipped, and
a silent check reports success. Mutation-tested both ways: answering row
89 on the screen only, and renaming a packet key.