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.
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.
Every map translated its own chrome but left the basemap's country, city
and street names in the style default (English on Mapbox, Latin + native
on OpenFreeMap) in all eleven locales. A reader in Arabic saw an Arabic
legend over an English map.
library/maps/localize-labels.js uses each engine's supported mechanism:
- Mapbox: map.setLanguage(lang). It also localizes the Standard style's
IMPORTED basemap, which a layer rewrite cannot reach.
- MapLibre: basemap symbol layers point at
['coalesce', name:<lang>, name:latin, name]. English restores each
layer's original expression, re-captured on every style load so a theme
switch never restores the previous style's labels.
Only layers whose text-field reads an OSM name field are touched. A
plugin's own label layers (zone titles, group names) are never rewritten.
useMapInstance applies it on every style.load and when the reader's
language changes (ProfileLoader's 'lang', else profile-config locale, else
?lang / <html lang>). The Prayer Walk Planner drops its private copy for
the shared one.
Measured on the Prayer Progress map, ar/zh/ru/hi:
- Mapbox getLanguage() == locale in all four.
- MapLibre: 13/14 symbol layers localized, and the plugin's own label
layer untouched.
Read in the Arabic screenshots: القاهرة, الإسكندرية, السويس, الأردن on
both engines. check:gallery-embed (+ :maplibre) and
check:prayer-walk-planner pass. The shared change reaches all 11 bundles
that compile initializeMap, and all are rebuilt here. check:shared-reached
marker basemap-labels-localized: 10/10 consumers.
library/maps/rtl-text.js installed the RTL text plugin for MAPBOX only
(window.mapboxgl, from api.mapbox.com). So every MapLibre map, i.e. every
keyless embed, drew RTL place names as disconnected, reversed glyphs.
Measured on the Prayer Walk Planner's printed Arabic sheet.
Under it was a second gap. Bundle entries call the installer at LOAD
time, before useMapInstance has lazily injected a MapLibre engine, so even
an engine-aware installer found nothing to install into.
- rtl-text.js is engine-aware. Mapbox is unchanged (same URL, same lazy
call). MapLibre gets the same plugin package from jsDelivr, keyless and
not api.mapbox.com, so keyless pages still make zero Mapbox requests.
New ensureRtlTextPlugin(gl) waits until it is LOADED, for callers that
capture a single frame (print).
- useMapInstance installs it the moment an engine global exists, the one
point every map passes through.
- The planner drops its private copy for the shared helper.
Measured: a print map of downtown Cairo (MapLibre, keyless) shows shaped
Arabic street and place names (شارع التحرير, مسجد الطباخ …); plugin
status "loaded", fetched from jsDelivr. check:gallery-embed passes js,
iframe, neighbour and linked (14 tabs); :maplibre passes;
check:prayer-walk-planner passes.
The shared change reaches all 11 bundles that compile initializeMap, and
all are rebuilt here. check:shared-reached gains an rtl-text-maplibre
marker (10/10 consumers carry it; cities-internet, gospel-resources and
my-upg-100-list boot maps another way and are not affected).
Also: the new Experimental bundle pplr-data-maps loads the Mapbox geocoder
under maplibre (pplrData.js:38). It is an owned, printed MAPBOX_KNOWN entry
routed to its owner; delete it in their fix commit.
ensureEngineCSS is only called by initializeMap. A bundle that imports
pieces of useMapInstance (ensureEngineScript, engineGlobal…) but never
initializeMap has ensureEngineCSS tree-shaken away, so the guard can
never appear in it and the race cannot happen there. The check counted
those bundles as missing the fix. upg100 caught it on my-upg-100-list.
The map-unmount-guard entry now also declares the unguarded shape. A
bundle is missing the fix only if it still compiles ensureEngineCSS
without the guard. With neither shape it is reported as not affected.
Entries without a staleRe behave exactly as before.
On today's tree: 7 bundles carry it, 2 are really stale
(doxa-research-map, research-dashboard), and 3 are not affected
(cities-internet, gospel-resources, my-upg-100-list). Before this, all
three were reported as stale.
The first regex for map-unmount-guard also matched useShadowStyles as
the current source compiles it:
if(!n)return;const i=n.getRootNode();if(!(i instanceof ShadowRoot)||…
The freshly built plugin-studio bundle, which has no useMapInstance at
all, matched it. So any consumer rebuilt against today's useShadowStyles
would have been counted as carrying the fix whether or not it did.
The marker now requires ensureEngineCSS's early exit, "…ShadowRoot))return",
where useShadowStyles continues with "||". Proven both ways on a rebuilt
doxa-simple-map: built from the guarded source it matches, built with
the guard stripped it does not. The studio bundle no longer matches.
initializeMap is async. If its component unmounts while it waits (a
studio tab switch, a composite swapping views), Vue nulls containerRef
and the next dereference threw "Cannot read properties of null (reading
'getRootNode')" from ensureEngineCSS. check:studio-all hit it about 1
run in 4 (gospel-dashboard); gospel-plugin found a second site one await
later (containerRef.value.clientWidth after the animation frame).
initializeMap now checks the ref after each of its three awaits and
returns silently when it is gone, and ensureEngineCSS returns on a null
element. An unmounted pane is normal, not an error, so nothing is
logged. The ref is the only liveness signal: requiring isConnected too
would silently stop maps that mount into a subtree before it is
attached.
Proof, deterministic in both directions:
- check:map-unmount-mid-init holds mapbox-gl.js, opens a map plugin in
the studio, switches away (destroying the pane), then releases the
script. Before: FAIL 2/2 runs (research-map, doxa-simple-map threw
the production error). After: PASS 2/2 across research-map,
gospel-dashboard and doxa-simple-map.
- check:map-unmount-race (gospel-plugin's browser-free test, adopted):
the guarded ensureEngineCSS resolves; with the guard stripped it
throws the exact message.
- No regression: check:studio-all passes 31/31, and 19 plugins still
create a live map canvas, as before.
check:shared-reached now tracks this change with a regex marker (a
control-flow fix has no literal that survives minification). It
reports 12 consumer bundles still to rebuild. Verified: rebuilding
doxa-simple-map moves it to "carries it".
The consumer list was derived with a plain substring test, and a substring
cannot tell an import from a comment ABOUT the component.
MEASURED: it reported research-dashboard as a stale consumer of
SemanticTreeLegend for three ticks, and I routed that "drift" to its owner as
work to do. It was a FALSE POSITIVE. research-dashboard imports the component
ZERO times and mentions it four times in prose -- DashboardFacetRail.vue:3 says
"the floating @map SemanticTreeLegend is DROPPED", followed by the root cause it
was dropped for. A bundle that deliberately removed a component is CORRECT to
lack its translation keys. The gate was reading the comment explaining the
removal as evidence of use, and the more carefully that removal was documented,
the more certain the false positive became.
★ A GATE THAT MANUFACTURES WORK FOR ANOTHER AGENT IS WORSE THAN ONE THAT MISSES
DRIFT. A missed regression is found by the next reader; an invented one costs
someone a tick discovering there was nothing to fix, and it discredits the gate
that raised it.
Now requires real import syntax -- `import ... from '<path>'` or a dynamic
`import('<path>')` -- matching either the @map path or any path ending in
/<Name>.vue.
MUTATION-TESTED THREE WAYS, because narrowing a detector can quietly blind it:
false positive gone -- research-dashboard no longer listed; legend-a11y goes
4 of 5 to 4 of 4, and every genuine consumer is
unchanged (PosterWall still finds all four)
real drift caught -- stripping the marker from doxa-simple-map's built
bundle fails it, exit 1
new consumer found -- injecting a real import into big-picture lists it
immediately as missing the keys
The second and third are the ones that matter: it would have been easy to fix
the false positive by narrowing until the gate saw nothing at all.
library/ is the single source of truth, but EVERY BUNDLE SHIPS ITS OWN COMPILED
COPY of the components it imports. So a one-line fix in a shared component is
live in whichever bundles happened to be rebuilt afterwards and silently absent
from the rest -- and nothing detected it, because every other gate measures the
RENDERED BEHAVIOUR OF ONE LEAF, and a leaf whose bundle is stale renders the old
behaviour perfectly.
Three independent instances in one week, which is what makes it a gate rather
than a note:
- the PosterWall hover outline (a7f57c1) reached my-upg-100-list and none of
the other three consumers;
- the SemanticTreeLegend legend.a11y keys reached four bundles and NOT
research-dashboard, so a legend there still ships nameless carets while
check:a11y-button-names passes 791/791 -- that gate only sees the caret on
leaves where an expandable branch happens to render. Nobody was working on
it. It had just been sitting there;
- commit 3cfe93c6, "Rebuild the fleet so the translated hint actually reaches
it" -- someone already hit this and fixed it by hand. The remedy was known;
the detection was not.
★ IT ASSERTS A MARKER STRING, NOT A TIMESTAMP OR A HASH. Comparing mtimes says
"older" and goes noisy on every unrelated rebuild. Comparing hashes cannot work
at all: a bundle legitimately differs from the library source it compiled. The
answerable question is narrower -- does the BUILT bundle contain THIS change? --
and a distinctive literal answers exactly that.
Consumers are DERIVED from the imports, never listed. A hardcoded list goes
stale the moment someone adopts a shared component, and a gate that silently
stops covering a new consumer is worse than none.
A marker that vanishes from SOURCE is reported as a stale ENTRY, not as failing
bundles -- otherwise reverting a change would blame every consumer for it.
MUTATION-TESTED IN BOTH DIRECTIONS, because a gate that has only ever failed is
indistinguishable from a broken one: rebuilding key-cities flipped its line from
✗ to ✓ and the count from 1 to 2 of 4, live; a positive control over a
long-standing marker reaches the PASS branch with all four consumers green; and
pointing an entry at a marker absent from source produces the stale-entry notice
rather than four false failures.
key-cities is rebuilt here because it was the mutation subject and the rebuild
is the fix the gate prescribes. The two bundles still red -- country-posters and
regions-dashboard -- belong to regions, and research-dashboard's legend drift is
older than this work; all three are reported, not rebuilt for them.