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.
The WALK string the facet gate injects into the page moves to a browser-free
module, internal/lib/facet-walk.mjs; the gate imports it, and
internal/check-facet-shape.mjs imports the same string and evaluates it on both
engines' source shapes without a browser. check:facet-shape ran from the browser
gate's own file, so the runner routed it to the browser slot; now it is headless
and joins gates:pre. Poisoned (the { geojson } unwrap disabled) it reports 4/5.
BUG-FACET-GATE-CANNOT-MEASURE-MAPLIBRE. The gate read source._data four times,
and the two engines store it differently -- cited from the bundles this repo
loads, the same constructor in each:
mapbox-gl v3.24.0 this._data = i.data the raw GeoJSON
maplibre-gl this._data = "string"==typeof i.data
? {url:…} : {geojson: i.data}
A GATE blind to MapLibre passes a map that is broken there, which is worse than
a plugin carrying the same bug.
★ ALL FOUR READS WERE BROKEN, NOT THREE. The census called :145 "guarded" because
it had `s._data || (s.serialize && s.serialize().data)` -- but `||` fires only
when the left side is FALSY, and {geojson: …} is TRUTHY, so the fallback never
ran. The wrapper has to be UNWRAPPED, not fallen back from. That correction is
the reason this is a shared helper rather than a copied one-liner.
__srcFeatures() goes in the WALK bundle -- the in-page script the gate already
injects -- NOT as an import of library/maps/pin-selection.js: all four call sites
run inside page.evaluate / page.waitForFunction bodies, which are serialised to
the browser, where a Node-side import does not exist. Same logic as
readSourceFeatures(), duplicated across a process boundary, which is the one
place duplication is not a smell.
A remote (url) source returns null, not 0: null means "could not measure", while
0 would mean "the map drew nothing", which is the finding this gate reports.
THE MAPLIBRE SKIP IS RETIRED. It exited 0 while announcing SKIP -- NOT MEASURED,
correct for the limitation but a gate that never looked at half the product. A
source present with an unreadable payload is now a real failure with a
diagnostic, because the known cause of that state is fixed.
check:facet-shape negative-tests the helper headlessly against both engine
shapes, in sr's check-pin-selection style, evaluating it OUT OF THE WALK STRING
so the thing tested is the thing injected. 5/5. Poison-tested: with the unwrap
removed, ONLY the maplibre case fails (4/5), so it catches the regression and is
not indiscriminate.
internal/ is the build owner's under rule 5B -- source sha to studio. Running the
gate itself launches a browser and is rega's.
Only the browser seat runs browsers, and only inside internal/browser-slot.sh.
That rule was remembered, not enforced: an agent ran a gate it took for a
headless source check and it launched Chromium outside the slot.
internal/lib/playwright-guarded.mjs is now the one way a repo file reaches
playwright: its launch(), launchPersistentContext(), launchServer(), connect()
and connectOverCDP() exit 75 (NOT MEASURED) with a message naming
browser-slot.sh unless DOXA_BROWSER_SLOT is set, and browser-slot.sh exports it
for the command it runs, so a bash -c grandchild inherits it. All 72 files that
imported playwright switch to it (internal/lib/browser.mjs and 64 internal gates;
plugin-designs scripts: doxa-research-map 3, my-upg-100-list 2,
research-map-plus-grid 2, research-dashboard 1): a mechanical import swap.
check:browser-gates-guarded (gates:pre) fails a direct import;
bun run browser-gates lists the 97 scripts that launch a browser. isBrowserGate
moves to internal/lib/browser-gates.mjs so the runner and the check share it.
Last tick I fixed the one executable literal the tree rename broke, and
wrote that the remaining mentions "will mislead the next reader but
break nothing." That second half was wrong, and worth correcting rather
than leaving.
Six of them are RUNNABLE COMMANDS — usage lines, a --leaf example, two
PROBE_PATH invocations, a BASE= export. Copy-pasting one aims a probe at
a folder that no longer exists, and the failure is silent: a 404 loads
as an empty document and every gate then reports whatever "nothing"
looks like in its own vocabulary. That is exactly how I produced three
confident fake findings in a single sitting last tick — a frame probe
reporting "0 images, no wall" on two different leaves, and a basemap
gate reporting "map never finished its style". A comment that names a
path a reader will then USE is not inert documentation.
So the six say <TREE_NAME> now, which is what the code reads and what
_tree-name.mjs exists to make authoritative.
WHAT IS DELIBERATELY LEFT ALONE, because rewriting it would falsify the
record: 19 mentions are historical narrative — "public/doxa-maps was…",
the census that motivated _tree-name.mjs, and check-catalog-identity's
citation of https://nuxtbase.app/app/doxa-maps/manifest.json returning a
200 sign-in page. That URL is EVIDENCE of a past false-200, not an
instruction; its code already reads TREE_NAME.
Live literals across internal/ are now zero, except public-hygiene's
doxa-life/doxa-maps, which is the private REPO name and correctly
unrelated to the tree.
Five gates took their measurement from whatever answered a default localhost
port. On this machine that port belongs to whichever worktree started first:
:4173 is wt-packet's server, :4898 is its repo root. Measured today — an hour of
probing reported on a build that was not on disk here, and the mistake was only
caught because a marker string was in the file and absent from the response.
"Answers 200" and "is serving this checkout" are different questions, and only
the second one licenses a verdict. _serves-this-tree.mjs already asked it (it
exists because the gospel-resources gate hit this and spent three runs reporting
on the wrong tree) but only three callers used it. Wired into the five that
measure a served page and had no such check:
check-provider-switch request tally per host — the wrong tree's tally
check-maplibre-layer-drops silent layer drops — invisible by nature
check-filter-width-stable width divergence — would accuse untouched CSS
check-facet-changes-map data flow on facet change
check-packet-browser imports the renderer FROM the server
Only a genuine MISMATCH skips. "No manifest to compare" is a different
precondition (an unbuilt checkout) that each gate already speaks for, and
turning that into a skip would silence these on the builds they should run on.
A mismatch exits 0, like the unreachable case, because it is a precondition and
not a product finding — but it prints which, so nobody reads it as a pass.
CONTROLS, each fired, per gate: correct server -> PASS with a real number
(provider-switch 19 -> 5 mapbox requests, drops 17/17, filter-width identical,
facet 2107 -> 1209 dots, packet-browser headings match); a served tree whose
manifest differs by one key -> SKIP naming "serves a DIFFERENT build"; no server
-> the pre-existing unreachable SKIP, unchanged.
One correction found by running it rather than by reading it: the first wiring
passed the LEAF PAGE URL where the helper wants the ORIGIN, so it looked for the
manifest under the page's own path and skipped every run for the wrong reason.
node --check cannot see that; only running both controls can.
KNOWN COST, not hidden: in a wave with no server of this tree running,
check:provider-switch and check:packet-browser now SKIP where they used to
report a green about another worktree. That is the honest reading, not a
regression, and the runner prints "evaluated nothing" for them. Next increment
is to let these gates serve this checkout themselves so they measure again.
Re-ran the unwired-gate census from last tick. check:facet-changes-map failed
with a clean verdict line, which looked like a real finding. It was not — it is
flaky: PASS, FAIL, FAIL, FAIL across four consecutive runs, failing with "the
map never populated after 90s".
Against the BUILT tree it passes: twice in the first check, then three more
times through the new script entry. Five for five. The product is fine.
A flaky gate is worse than a failing one. It teaches you to re-run until green,
which is how a real red later gets dismissed as "just the flake".
The cause is the default target. A dev page load pulls 88 unbundled module
requests against the built leaf's 3, and Vite compiles them on demand, so with
several agents working the same box the wait this gate ALREADY retries three
times at 90s can still lose. Raising the timeout is the wrong fix for exactly
that reason: it is not one slow moment, it is contention.
The dev default is NOT changed. Its header gives a sound reason — the dev
surface always reflects current source — and another agent may be relying on it
for hand-running uncommitted work. Instead the flakiness, its cause and the
deterministic invocation are recorded on the script, and
check:facet-changes-map:built makes the built-tree run a command rather than
something to remember. Also recorded why this gate is not wave-wired: a step
green 1 run in 4 would train everyone to ignore the wave.
One dead end worth recording. I tried to reverse-engineer the wait by locating
the map instance myself and got "never found" on BOTH surfaces — my selector was
wrong, not the product. The gate has working discovery of its own; I stopped
probing and used its result, which was already decisive.
Run against ?provider=maplibre the gate reported
FAIL — the map never populated (no rd-bubbles features after 90s)
about a dashboard that visibly renders all 2107 dots with a working
choropleth. It reads the source's live GeoJSON off `_data`, a private Mapbox
internal. MEASURED on one dashboard, one build, only the engine differing,
by calling the app's own setData() to shrink 2107 features to 5:
mapbox _data 2107 -> 5 _options.data 2107 -> 2107 (_data is LIVE)
maplibre _data never an FC _options.data 2107 -> 2107 (NEITHER is live)
So on MapLibre no private field tracks setData and the wait can never
succeed. It now diagnoses the engine and SKIPs with the reason. Same
false-FAIL shape removed from check:filter-width last tick: a control that
accuses correct code is worse than no control.
★ DELIBERATELY THE SMALL FIX. Re-expressing the assertion on the public
querySourceFeatures() would change the gate's reported counts, and the two
earlier attempts at that made things worse -- one failed BOTH engines by
preferring the construction-time _options.data, the other called a
node-side helper from inside a page-evaluated string where it is simply
undefined. Every line of page code added here is self-contained for exactly
that reason.
THREE CONTROLS:
maplibre -> SKIP, exit 0, naming why it cannot count
mapbox -> PASS, exit 0
--self-test -> gate REJECTED the reverted build, catching
'circle-color is the flat literal "#ef6c00"'
The third is the one that matters: the gate still catches the bug it exists
for rather than having been quietened.
This removes the LIE, not the GAP. MapLibre dashboards remain unverified by
this gate; BUG-FACET-GATE-CANNOT-MEASURE-MAPLIBRE stays open, downgraded to
P3 and re-scoped to the coverage question.
The panel destroys and re-creates its Map on a re-render, and the dev server's HMR
does this freely, which leaves window.__rdashMap pointing at a torn-down instance
whose getSource() throws "Cannot read properties of undefined (reading
'getOwnSource')". Measured one run in three failing that way on the dev surface
while the product was fine — the first invocation through the package.json script
failed for exactly this reason and the next passed unchanged.
A gate that reports a product bug when its own reference went stale is a gate that
gets switched off. __live() now proves the handle still works before any assertion
reads through it, and readState() waits for the re-created instance to repopulate
rather than treating a dangling reference as a verdict. A state that is still
stale after the grace period is reported as an infrastructure fault, named as such,
not as a filter failure.
Verified 4/4 consecutive passes on the dev surface where it previously flaked, and
all four negative-test modes still catch their regressions (the retry must not mask
a real failure).
The prayer filter was reported as "not applying" to the map. Measuring it first
changed the fix: the filter was never broken. Selecting "Has Prayer" already
narrowed the rd-bubbles source 2107 -> 689 features, exactly matching the
dropdown's own tally, disjoint from "Needs Prayer" (1418), summing to 2107 and
round-tripping on clear.
What was broken is that circle-color was the flat literal '#ef6c00' — read back
off the live layer, the bare string, not an expression — so every dot painted the
same orange whether it had prayer or not. A prayer question got an answer with no
prayer information in it, and the surviving dots looked identical to the ones the
user started with. That is why a working filter reads as "not applying".
This gate asserts all three halves, because any one alone is a false pass:
1. the selection changes WHICH dots are on the map, by count AND by identity
2. every surviving dot satisfies the level that was selected
3. while a prayer filter is active the colour is a data-driven expression
reading peoplePraying, not a flat literal
Assertion 3 is the one that matters: 1 and 2 were already green while the bug was
visible. Identity is checked as well as count because the filter removes from the
tail of an alphabetical list, so the first five slugs are byte-identical before
and after — a gate reading the head of the list would report "unchanged".
NEGATIVE-TESTED in four modes, each isolating one assertion, all verified caught:
colour reverts only the paint (the reported bug) -> caught by assertion 3
props strips peoplePraying -> caught by assertion 2
frozen blocks the source update entirely -> caught by assertion 1
both reverts the lot -> caught
A gate that cannot fail is worse than no gate.
Verified on both hosts against the BUILT tree: the cap-5 shell and the standalone
leaf. Browser gate, so not in build:unlocked.