On 2026-09-29, 41 serve-strict processes had outlived the gates that started them: 279 MB resident,
the oldest 5.3 days, spread across eight worktrees. A gate that crashes, is killed, or is refused by the
browser-slot guard's process.exit cannot run its own srv.kill(), and _serve-reaped's handlers cover
signals and normal exit but not a SIGKILLed parent.
serve-strict takes --exit-with-parent: it records the pid that started it and exits within 2 s once
that process is gone. Opt-in, so a server someone deliberately left running is never taken down.
Every internal/ gate and tool that spawns it now passes the flag (14 files, plus _serve-reaped, the
shared helper). Proven: a parent that spawns the server with the flag and exits abruptly (as the
guard does) leaves it alive for a moment and gone within 3 s.
The orphans already running belong to other worktrees' runs and are left to their owners; plugin-local
scripts that spawn serve-strict should pass the flag too.
These land with the change that would otherwise hollow them out. Both gates
reached the Mapbox engine by filling a token into the published snippet, which
worked only while a token selected the engine. It no longer does, and the
snippet now pins provider explicitly, so both Mapbox arms would have run
MapLibre and still passed.
Both now name the engine. The shelf gate REPLACES the provider key rather than
prepending a second one: a duplicate key parses to whichever came last, which is
a coin-flip that happens to work today.
The more useful half is the vacuity check, because the old assertions were all
of the form "no request used the wrong token" — trivially satisfied by making no
requests at all. Each Mapbox arm now fails if it contacted Mapbox zero times.
In the shelf gate the two flags are twins set from one expression, so an arm
cannot be exempt from both: under maplibre zero requests are required, under
mapbox at least one.
Without that pair, the day a Mapbox arm silently runs MapLibre both arms pass
and the gate measures one engine twice.
I cannot run either: both are Playwright and the browser belongs to rega. Syntax
checked, the browser-gate guard still passes across 405 files, and gates:pre is
green — but the behaviour these assert is unverified by me and needs rega.
check-experimental-shelf imported resolve(ROOT, 'node_modules/playwright/index.mjs')
dynamically, named no package specifier, and so launched Chromium outside the
slot while check:browser-gates-guarded passed it (upg100 found and measured it).
It now imports the guard, and an unslotted run exits 75. The check also fails a
path to node_modules/playwright (or playwright-core) anywhere in code; with the
old import restored it fails that file.
generate-tree's preload comment now ends "...the map draws with MapLibre and this line must go", but the
gate still looked for "remove this line", so under MapLibre it kept the preload and every arm reported a
Mapbox request that no real MapLibre host makes (rega, 1173a992). It matches either wording now.
Every earlier assertion counted FEATURES, and queryRenderedFeatures answers happily over a white
rectangle (gospel's incident, 0574fe7f). Each map surface now also passes two looks:
- COLOURS read off the map's own WebGL canvas inside its 'render' event (floor 40), so a legend
or panel over the map cannot inflate the count and no per-plugin clip is needed;
- FILL = the map's height over the plugin ELEMENT's (floor 70%), not canvas over container,
which stayed 1.0 when a stylesheet collapsed both together.
Each arm has its own self-test poison and must be the one that catches it: 'blank' (every layer's
opacity 0) -> "only 1 colour(s) on its canvas ... while 12204 features still count as drawn";
'collapse' (container squeezed to 300px) -> "fills 38% of <all-people-groups>'s height".
Drawn counts are now read SETTLED (equal on two consecutive polls): a first-non-zero read took
1752 of 2107 mid-fit on Cities. The PASS line names only the surfaces that ran.
Measured: 7 bundles, 60 rows PASS, 3 n/a; lowest paint across the 40 map rows 264 colours, 92%.
The output tree's name has always been configurable (vite.doxa-maps-build-config.json "name"),
but 61 tooling files ignored it and hardcoded 'doxa-maps' (181 literals), so a rename would have
moved the build while the gates kept reading the old folder.
- internal/_tree-name.mjs reads it once (validated as one URL-safe path segment; falls back to the
historical name) and exports TREE_NAME / TREE_NAME_RE; `node internal/_tree-name.mjs` prints it
for shell scripts.
- A codemod rewrote 171 literals in internal/ to TREE_NAME; the regex literals, messages with
backticks, atomic-public-swap.sh (fails loudly if the name cannot be read), build-lock.sh and
vite.config.js (its duplicate readMfeName is gone) were done by hand.
- The name is unchanged: old and new tooling produce all 1408 files under public/ byte for byte.
Code lines still naming the tree in internal/ + vite.config.js: 2, both intended (the helper's
fallback, the private-repo-name pattern in public-hygiene-check).
This renames nothing visible; the eventual rename becomes a one-line config change.
Two surfaces join the leaf and the Gallery, each built from the manifest's own snippets so a
broken snippet fails as well:
B a host on a different origin iframes the leaf; the plugin must draw its data and the
host's title, URL and scroll must not move.
C a host on a different origin pastes embed.script with its own token and provider. Under
maplibre: 0 api.mapbox.com requests. Under mapbox, where the plugin draws a Mapbox map, a
second run with a fake host token must show that token, and no other, in tile requests.
MapLibre-only plugins take no token and report n/a.
The self-test now also poisons the bundle under the script embed. Rows print their seconds;
--surfaces A,B,C,D splits the ~26 min run. Measured: 7 bundles, 60 rows PASS, 3 n/a.
B does not assert 0 api.mapbox.com: every generated leaf head links Mapbox JS/CSS whatever
the provider, which is a template property reported separately.
The Driver asked for "all experimental plugins working"; this makes it a
measurement. The gate derives the shelf from the Research Gallery's own rules
(manifest minus curated tabs and NOT_EXPERIMENTAL), then checks every bundle on
its leaf page and on its Gallery tab, under mapbox and maplibre. It requires
content inside the plugin's own element, 0 page errors, 0 failed requests, no
loading text, and for each map 'load' fired AND its own GeoJSON data drawn. An
empty source does not count. Maps are found by wrapping the engine's Map class
in an init script, so no bundle changes for it. It serves this checkout on an
OS-assigned port, and self-tests first: a deliberately broken bundle must fail,
or it exits 3.
Prayer Walk Planner's data is the user's own (zones in localStorage), so the
gate seeds one zone and requires it drawn, printing the seed and reason on every
run rather than exempting the bundle.
It found two real defects on the way. Both are fixed here, and both failed the
gate before the fix:
- UPG Neighbor: every label layer asked MapLibre's default fonts (or Mapbox's
DIN) from OpenFreeMap, which 404s, so no label drew under maplibre. labelFont()
now picks Noto Sans under MapLibre; Mapbox is unchanged.
- Prayer Walk Planner (always MapLibre): zone titles never drew, for the same
reason. The label layer now names Noto Sans Regular.
Result: 28/28 PASS across all 7 shelf bundles.