Every reference to the Driver in library/ is reworded: the engineering reason
stays, the speaker and the quote go, and a decision still open reads
"provisional, pending a product decision". Comments only: each changed file
is identical after stripping comments (JavaScript, template and CSS, including
CSS comments inside the style strings shadow-DOM components inject).
Those style-string comments do ship, so several bundles change by exactly
those comment bytes.
The live API sends imb_gsec as { value, label }, not a number. describeGroup read it
with Number(), got NaN, matched no GSEC level, and dropped the level sentence — not an
error, not a fallback, simply absent, so the row described a people group nobody has a
reachedness figure for. upg100 hit it and fixed their own normalizer; that fixes their
caller and nobody else's, and every other consumer handing this function a RAW API
record lost the sentence the same silent way.
The unwrap belongs at the READ, because this function's contract is "an API or
normalized row" and both spellings are inside that contract. pick() now flattens
{ value, label } to its value, exactly as normalize.js:22 does.
THE LANGUAGE CLAUSE WAS GOING THE SAME WAY, FOR THE SAME REASON, and fixing only GSEC
would have left it. On a raw record the language NAME is at primary_language.label; the
field itself holds an ISO code. The existing read deliberately refuses that code -- it
would print "and speak abf" -- so a raw record simply had no language and fell to the
country-only sentence. labelOf() takes the display half of the same pair, and ONLY for
a nested object: consulting a flattened primary_language would print the code, which is
the defect the refusal exists to prevent. Same for country_code, whose nested label is
also the English fall-back when no resolver is injected.
A raw record now produces a string identical to the normalized one, which is the
assertion that makes the rest of it hard to regress.
Negative-tested: removing the unwrap, dropping either nested-label read, or letting
labelOf read bare strings (so the code prints) each turn it red; restored, green.
describeGroup(group, locale, opts) -> one localized sentence pair for a people
group's profile row: where they live, what they speak, and how reached they are.
Pure, exported, no refs or fetch; strings and the country resolver are injected.
The level is phrased from GSEC, a number, never from GSECDescription, which is
English prose the API hands us -- interpolating it would leave the widest column
of every non-English packet in English.
THE LANGUAGE FIELD WAS BOTH WRONG FIELDS AT ONCE.
primary_language is in normalize.js's NESTED_FIELDS, so the API's { value, label }
is flattened to its VALUE: an ISO 639-3 code. Reading it prints "and speak abf".
The name is in primary_language_label, and it arrives INDEX-ORDERED -- "Arabic,
Shihhi" for what a reader calls "Shihhi Arabic". 725 of the 12,379 rows carry an
inverted label, one row in seventeen.
The un-inversion is not a second copy: useLanguageFamilyLegendData already built
one to make a lookup key, so the split moves to library/utils/languageLabel.js and
both call it. Doing that surfaced why the two uses are not the same use -- 13 real
names carry a comma INSIDE a parenthetical ("Boano (Indonesia, Central Sulawesi)"),
and a naive split reverses those into "Central Sulawesi) Boano (Indonesia". As a
lookup key that garbage simply misses and the next candidate is tried; printed in a
sentence there is no such safety net. The shared split is top-level-only, and a
trailing parenthetical is hoisted past the reversal so "Naga, Tangkhul (India)"
reads "Tangkhul Naga (India)".
The legend's own behaviour is unchanged, checked rather than assumed: old and new
lookup compared over all 3,498 labels in langFamilyByLanguage plus every
LanguageName in the dataset, 0 differences.
countryName.js is NOT imported. It resolves only through the Vite '@map/' alias and
imports two JSON files without an import attribute -- fine in a bundle, fatal to a
node gate. Importing it would make this module unloadable by the test that proves it
right in eleven languages, so the renderer passes it in, and a missing injection
degrades down countryName.js's own ladder (localized -> English label -> alpha-3)
rather than blanking the clause.
check-describe-group asserts every GSEC level, each missing field, both locales,
truncation, the inverted label, the parenthetical that must not be shredded, and
that a bare language code is dropped rather than printed. Negative-tested: reading
primary_language, skipping the un-inversion, a naive split, and dropping the
parenthetical hoist each turn it red; restored, it is green.
Not yet wired into the renderer and not yet in gates:pre -- register with
"check:describe-group": "node internal/check-describe-group.mjs".