Comments in library/ and internal/ that quoted a person, attributed a decision to one, used first person,
named fleet agents or carried private host names now state the same technical fact in neutral words:
poster-render (including the comments it writes into generated posters), packet-render, the poster and
research scripts, PosterWall, DataTable, the legends, the layouts and the gates. The network-policy note
states the policy itself. Behaviour is unchanged: a few printed gate messages and reason labels drop an
agent name, the poster-render integrity pin follows its comment-only edit, and series.json changes only in
its _meta and _why notes. The historical poster design snapshots (library/scripts/posters/snapshots/),
which nothing reads, are removed.
Every reference to the Driver in internal/ is reworded the same way as in
library/: the reason stays, the speaker and the quote go, an open decision
reads "provisional, pending a product decision", and agent names and decision-
sheet rows are dropped. Where a gate printed the word, its message now names
the reviewer or the ruling instead.
Three spots were code, not comments, and changed with their callers:
- the element census's allowlist field `driver` is now `ruling`, in the gate
and both presets (same validation: match, ruling, date and card required);
- check:i18n-never-translated's DRIVER-GATED marker is DECISION-PENDING, in
the entries and in the regex that counts them (its output is otherwise
byte-identical);
- the package.json description of the census says "ruling".
public-hygiene-check keeps its two patterns for the word: they are what keep
it out of the served folder.
GOSPEL'S LEGEND RENDERS IN THE BROWSER and needs these zones at runtime; a
build-time import path would not reach the bundle. data/tables/ is the pool the
tree copies into public/dx-maps/assets/tables/, which is how near-neighbors.json
already reaches a browser, so this makes the zone data a single source the same
way (dx-shepherd, 2026-09-28).
admin-to-list.json deliberately does NOT move: it waits on the Driver's row 87,
the "zones fold" regeneration.
★ TWO ROOTS IN internal/check-list-selection.mjs, NOT ONE. Its readJson helper
reads all three files through a single DATA constant, and only two moved.
Collapsing them to one root silently breaks whichever file is not where you
pointed — so the helper now routes admin-to-list.json to the plugin dir and the
other two to data/tables/, with the reason in the comment.
★ THE ASSET-POOL COPY IS MANUAL, AND IT HAS ALREADY DRIFTED. There is no
automated copier: `git grep` finds no rule in vite.config.js or internal/ that
writes public/dx-maps/assets/tables/, and exp's rings note says the same ("only
the tree copies tables"). Measured before copying: data/tables has 309 json
files, the asset pool 304 — and the five missing were groupings.json,
zone-regions.json AND THREE THAT PREDATE THIS COMMIT (index.json,
open-doors.json, platform-reachability.json). So these two would have joined an
existing gap rather than created one. Both are copied here and verified with
cmp; the other three are reported separately, not silently fixed in a move
commit.
Paths verified to resolve, not assumed: P(GROUPINGS) and the REGIONS join both
resolve to existing files. Gates green after the move: check:list-selection,
check:selector-fields, check:describe-group, region-hue-proof.
upg100 filed asia-india-madhya-pradesh and this check went red, which is the arm
working. They reported it instead of deleting my expectation to make their own
change green, and they were right that doing so is how a guard gets hollowed
out. dx-shepherd ruled the two land together, which is the only ordering where
neither of us is red in between.
THIS COMMIT IS RED ON MAIN BY ITSELF. It expects three unfiled zones and today's
data has four. It goes green with upg100's data change and not before, so the
two must land in the same landing.
The gap pin drops to guizhou, xinjiang and bihar. Filing is the good direction
but it is exactly as revertible as any other data edit, so the three newly filed
India zones now get an arm of their own: drop a key from zone-regions and those
groups quietly leave the region tree again, with the bucket the only thing still
carrying them. Two of the three were never visible to the gap arms at all, so
without this they could be unfiled with nothing to say so.
Also recorded what the gap number actually counts. It is admin-to-list's values,
which is not every printable zone, so it is a lower bound rather than a census.
upg100 proved that costs something: checking the property instead of the four
names they were handed, they found four more unfiled zones, none of them an
admin-to-list value, so none of them ever appeared in this number. A count that
is quietly a lower bound reads exactly like a complete one.
And the fixture arm no longer hardcodes the number four. The gap changes; a
literal there turns every legitimate data fix into a confusing red in an arm
that is not even about the data. It counts the pin instead.
gospel found a latent ambiguity in the emit contract while wiring, and checked
it instead of building past it. selectedId emits one bare string with no kind,
and region ids and zone ids share a namespace. Re-measured here rather than
taken on report: 33 zone ids, 7 region ids, and the intersection is exactly
europe, whose region is itself. So the string "europe" cannot tell a consumer
whether the Europe region row was clicked, meaning every European zone, or the
Europe zone leaf, meaning that one.
It is not live today, and only for a reason that is a coincidence rather than a
design: region europe contains exactly one zone, so both readings give the same
set. It goes live the day a second European zone is filed, and then it fails the
quiet way this whole file exists to catch — a smaller result, no error, no empty
state.
So this is an alarm, not a fix. The consumer builds the tree, so only the
consumer knows which rows are regions, and gospel is namespacing theirs. Two
arms: the colliding set is pinned and says re-derive if it changes, and each
colliding id's region must still contain exactly one zone. The second fails with
the instruction rather than the symptom, naming the zones and telling whoever
hits it to namespace their region rows.
Both falsified before trusting them: filing a second European zone fires the
ambiguity arm with both zones named, and adding a second colliding id fires the
drift arm with both ids listed. Data restored, clean diff.
The same note is on selectedId, because the next consumer to build a tree from
this zone data inherits the collision, and the doc as written invited it.
Every assertion here ran against a fixture. That proves the module behaves and
proves the fixture models a gap; it does not prove the gap still exists in the
data we ship. So the negative control could have stayed green forever while the
bucket quietly became dead code — or while a fifth zone fell out of the region
tree and nobody heard. Evidence that lives somewhere other than the thing it
describes has already stopped being evidence, and looks identical.
Three arms now read the real files. The gap between the region tree's source and
the joinable set is measured, and it is exactly the four this check names:
guizhou, xinjiang, bihar, madhya-pradesh. xinjiang is asserted specifically —
joinable in admin-to-list, absent from zone-regions, so its hundred groups are
reachable only through the bucket.
Two of the arms fail ON PURPOSE rather than going quiet. If every joinable zone
becomes filed, the check refuses to pass and says to re-derive the bucket or
delete it with its component, because an arm guarding nothing is not a pass. If
the gap moves in either direction it prints both sets and names the zone that
changed: one added is groups that just became unreachable, one removed means the
fixture is stale.
Both were falsified before being trusted. Filing all four turns the inert guard
red; dropping gujarat from the region tree turns the drift arm red with gujarat
named. Data restored, clean diff.
This deliberately does not resolve the three disagreeing lists — 33 filed, 12
joinable, 14 grouped, disagreeing in both directions. That is a data-ownership
question, and deciding it here would be the silent join we have refused. The
check only records the difference so it cannot drift unannounced.
Six more assertions, and a package.json entry so gates:pre actually collects
this instead of it only ever passing when I run it by hand.
Two facts the legend contract left implicit, both of them the quiet-wrong-answer
shape rather than a crash:
Ticking every REGION does not select every ZONE. The four unfiled zones have no
region by definition, so a reader who selects all seven regions still does not
have xinjiang and its hundred groups — and nothing on screen says so, the total
is just smaller. The bucket makes those zones reachable; it does not make them
reachable by a region sweep. A select-all must therefore sweep zones, not
regions, and this fails if one ever sweeps regions.
A zone whose members do not resolve must render EMPTY, not disappear. Members
resolve for sixteen of thirty-three today; five await a field and nine have no
selector anywhere. A tree that hides what it cannot join tells the reader the
zone does not exist, when the truth is only that we cannot currently join it.
Presence is by id, never by member count.
Both arms were falsified before being trusted: filtering zero-member children,
and mislabelling the bucket as an ordinary region row, each turn four of these
green. The negative control still carries the rest — it asserts the unaugmented
tree really does drop xinjiang, so the bucket is demonstrably load-bearing
rather than decoration.
The thirteen assertions were in a _qa-cycle probe, which this repo gitignores.
They ran green on my disk and would not have landed with the component, so the
code was about to ship untested while I reported the tests as evidence for it.
studio caught it while staging.
That is the failure this very file guards against: a green result measured
somewhere other than where it matters. A probe proves something once; a
registered check keeps proving it.
Same assertions, registered path, relative import corrected. It needs a
package.json entry to join gates:post, which is studio's to add.