check:composite-freshness found stale on main: all-people-groups
big-picture
cities-internet
country-posters
dashboard-plus-grid
demo-map
doxa-research-map
doxa-simple-map
key-cities
my-upg-100-list
people-groups-grid
pplr-data-maps
prayer-walk-planner
regions-dashboard
research-dashboard
research-map-plus-grid
template-bundle
upg-neighbor
wall-of-people-groups. Rebuilt, tree + SRI
regenerated; the landing precondition (fresh, one folder, SRI) passed in main-capture scan.
Each leaf's dev page must hand its element assetsBase "/assets"; a boundary file and a table under
that base must answer 200 with bytes that JSON.parse (never an HTML fallback), and a bogus file
under it must be a real 404.
Reported from a laptop running `bun run dev`: under the Plugin Studio, maps drew with no boundary
polygons and UPG-100 had no posters. The built tree was fine, and every gate measured the built tree.
Cause: a dev leaf page is /__leaf/<bundle>/<profile>. A plugin that falls back to the page-relative
'./assets' asked for /__leaf/<bundle>/assets/…, and the leaf route answered with its own HTML at
HTTP 200, so JSON.parse failed and the map drew without our data. /<tree>/assets/ was not served
in dev at all. The built leaf never hits this because its page passes assetsBase "../../assets".
- dev-plugin serves the pool at /__leaf/…/assets/ and /<tree>/assets/ too, and the dev leaf page
passes assetsBase "/assets" in its profile-config, as the built leaf passes its own.
- resolveAssetsBase's last rung is '/assets' under the dev server (import.meta.env.DEV, false in
every build); the built tree keeps './assets'.
- check:dev-assets (headless): starts a short-lived dev server for this checkout, requires
boundaries/previews/fonts/images/tables to answer 200 and not HTML at every prefix a dev page can
produce, and requires the dev leaf to carry the root-absolute assetsBase. It fails on the parent
commit (4 prefixes and both leaves) and passes here.
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.
The rows under "No resource found" carried a name, a language and a population
and nothing else, so the question a mobiliser opens a country to answer — what do
I do about these? — cost one click per group. 369 of the 370 groups with nothing
have a way in, and it was reachable only by opening each pin in turn. Mali's two
now read "way in: French"; Benin's one reads "way in: SUN (signed)".
Text rather than a link, and it could not be otherwise: these rows are button
drill-ins and HTML forbids an anchor inside a button — the parser hoists it out
and it stops being a link, which is why the rows that DO have resources are plain
containers instead. The row still opens the group sheet, and the sheet carries the
bridge's real links.
It names the bridge and claims nothing further. "Bambara" here does not say
Bambara reaches this group; that stays the local question the sheet spells out.
It turns a list of dead ends into a list with a first move against each one.
BUILD-INTEROP-CONTRACT-GRID-UNDER-MAP-BOTH-PROVIDERS asks for a provider matrix:
the same assertions, once per engine, with identical counts. Interoperability
that holds on one provider and not the other is not interoperability.
It is not a formality here. The map half of this link resolves a religion family
through its own legend, and the legend is drawn by the map engine — MapLibre
drops an entire layer in silence for one misplaced paint property, which would
surface as a link delivering nothing while the Mapbox run stayed green.
So the gate now runs the whole measurement under ?provider=mapbox and
?provider=maplibre, and then asserts the two agree: the same starting total, the
same rows for the same family, and the same rows over the composite's wire. A
per-engine pass with different numbers would mean the same choice narrows to
different peoples depending on which engine a partner embedded.
MEASURED, identical to the row on both:
mapbox 2,107 groups · Buddhism 83 direct · 83 linked · mutant 0
maplibre 2,107 groups · Buddhism 83 direct · 83 linked · mutant 0
The mutant still runs first on each engine, so a green here still means the gate
can tell a right mapping from a wrong one — twice over now.
The output tree was renamed doxa-maps → dx-maps. The name has always
been a config knob, and _tree-name.mjs exists precisely so a rename is
one line — but three gates still built their URLs from the literal, so
they fetched a folder that no longer exists.
THE FAILURE MODE IS WHY THIS MATTERS MORE THAN A PATH FIX. A gate
pointed at a missing tree does not error; it loads a 404, finds an empty
document, and reports whatever "nothing" looks like in its own terms.
Measured in one sitting, all three of these were artifacts of the rename
rather than findings about the product:
a frame probe returned 0 images and no wall, twice, on two different
leaves — which I first read as "the composite mounts the wall in a
second pane";
check-basemap-labels-localized returned NOT MEASURED, "map never
finished its style";
and every leaf under my plugin answered 404 while the card-fits gate
had been passing, because IT was already migrated and my probes were
not.
Only the refuse-to-judge guards kept those from becoming filed defects.
WHAT WAS ACTUALLY LIVE, after separating comments from code: exactly ONE
executable literal across the 27 files that still mention the old name —
the --leaf default in check-gospel-rtl-shaping, which I added last tick
when parameterising that gate for the other raw-map owners. The rest are
prose, plus one in public-hygiene-check that is the private REPO name
(doxa-life/doxa-maps) and correctly unrelated. So the fleet-wide
migration was essentially complete and this closes it.
check-leaf-english-leaks now imports TREE_NAME for both its reachability
probe and its leaf URLs; check-poster-card-fits was already migrated in
code and only its comment still described the old path.
check:composite-freshness found stale on main: all-people-groups
cities-internet
demo-map
doxa-research-map
doxa-simple-map
key-cities
people-groups-grid
pplr-data-maps
research-dashboard
template-bundle
upg-neighbor
wall-of-people-groups. Rebuilt, tree + SRI
regenerated; the landing precondition (fresh, one folder, SRI) passed in main-capture scan.
"Nothing found 370" is where the legend stopped, and it reads as a wall. Every
one of those groups but a single uncoded row now has somewhere to start — a
curated bridge, a language their own country actually speaks, or SUN for a Deaf
group — and none of it was visible without opening 370 pins one at a time.
It says "a possible way in", not "reached". A bridge has to be learnt, it is not
Scripture in their language, and the red dot means exactly what it meant before.
Counted in the same pass as the tier numbers, from the same rows, so the two can
never drift apart.
Also drops a link-pruning pass added earlier in this change and then removed. It
was written on the belief that 74 links recorded broken were still being served;
they are not — the builder has pruned them from the index for some time, and it
reports the count on every run. The measurement that looked alarming was a
verification record listing URLs that had already left the index. Nothing was
wrong, so nothing needed fixing, and a second implementation of a working rule is
worse than none.
Their CountryMap binds `description: p.imb_people_description ?? ''` and asks for
that field in PIN_FIELDS (master, :89 and :114). The doxa-maps-build branch this
repo's DX-life checkout is parked on still says location_description, and that is
what we cloned — I even kept it deliberately last commit, reasoning that "the
popup needs it", which was true of the branch and false of their site.
MEASURED against the prayer API over 2,107 people groups:
non-empty location_description 35 (1%) <- what our popup rendered
non-empty imb_people_description 2107 (100%) <- what theirs renders
So this was not the wrong paragraph, it was NO paragraph: roughly 2,072 groups
opened a popup with a blank description where doxa.life shows prose.
Nothing caught it because the popup did open, with a name, population and
religion — my own pin probe asserts that a popup APPEARS, never that it says
anything. An element can be present and empty, and "it opened" is not "it is
right".
The verification queued behind this: _probe-popup-description-field.mjs clicks a
pin, reads the group's NAME out of the popup, asks the API for BOTH candidate
fields for that exact group, and asserts the popup contains
imb_people_description and NOT location_description — the second half is the
control, since "the popup has a description" passes with either field. It reports
UNPROVEN when the two fields happen to be equal for that group. Browser slots
have been saturated for fifteen minutes, so what is proven here is the data path:
the shipped bundle requests imb_people_description, carries no
location_description at all, and the two fields demonstrably differ in the API.
Built only regions-dashboard.
Two gaps in the build-owner tooling, found by rega while landing 13f5f3a3:
- check:composite-freshness judged only COMPOSITES. A library change (bd75f609's Arabic catalogue:
library/i18n eager-loads every locale into every bundle that uses it) left 12 of 25 bundles stale on main,
most of them not composites; my consumer rebuild d1af039a saw none of it. The check takes --all and
--bundles a,b; the landing precondition scopes it by what the landing changed (shared code -> every
bundle; otherwise composites + the bundles whose own folder changed); the build-owner landing always
uses --all. Proven: at d1af039a it names prayer-walk-planner STALE (0b695f70 vs 0042dda2, rega's numbers);
on main 13f5f3a3 --all finds the 12.
- fix:sri repaired manifest.json but not the Research Gallery's byte-identical copy
(doxa-maps-spa/manifest.json, read first on a foreign origin), so an embedded gallery refused a rebuilt
bundle; it now rewrites the copy too (tested: a changed bundle -> both repaired, identical, check:sri
PASS). And generate-tree now writes the front page's integrity itself, so `bun run tree` after fix:sri no
longer strips it (tree alone: 115 agreed, 0 added).
The shared root-dx-life checkout is parked on doxa-maps-build (9305706, Sept 17),
not DX-life's master (fc4d622, today) — so every file I have read for a RULE 1
port came from a divergent branch, not only the translated strings.
I checked the nine sources I had ported from. Six are identical on master, and
the three that differ are identical in the exact rules I relied on: the uupg
country-and-bloc markup line I matched this morning, the whole .country-map
block, and the .uupg__card 576px reflow. So the clone conclusions hold.
One thing did not. That branch renders <p class="uupg__content"> with
location_description and then hides it with display:none; master has removed the
markup, the rule and the field. We had cloned the hidden version faithfully,
which meant asking the API for location_description on every list request purely
to fill an element nobody can see. All three are gone here too.
The pin POPUP still needs that field and still asks for it — that is
region-dashboards.vue's own FIELDS (:176), a different request, and their
CountryMap really does render it. It is not a search key either
(SEARCH_KEYS is their list verbatim), so nothing else was reading it.
Nothing visible changed, and that is the point: the census still measures 42
strings against their 42 on the country page, 0 unaccounted, 0 omissions.
check:sri PASS. Built only regions-dashboard.
_qa-cycle/02-builder-cycle/dxlife.sh now reads DX-life from github/master via
git show, never from the shared working tree, and exits 75 NOT MEASURED if it
cannot resolve master rather than silently falling back to the stale one.
A library change merged into main reached the planner without a rebuild (main's own source
builds 0042dda2..., main committed 0b695f70...). Only the planner is rebuilt (its owner);
its SRI is refreshed in the manifest AND the gallery's cross-origin manifest copy, which
fix:sri alone leaves behind.
THE RENAME. The built tree is published as dx-maps now, and both gates had
public/doxa-maps hardcoded. A wrong tree name does not error, it 404s — and a
gate that 404s reports a clean page, which is the most expensive kind of green.
They read TREE_NAME from the one helper instead, so the next rename moves them.
★ AND THE RE-VALIDATION THE REPAIR DEMANDED CAUGHT A SECOND BUG — IN THE GATE.
Re-running the clustering gate against its own baseline, Network came back at
1,700 lines: exactly MST's number, where the same build had given 23,896. Not a
regression. The gate settled on "two consecutive reads agree", and the previous
mode's lines stay on the map for as long as the new mode takes to compute — so a
stable read can be the OLD picture. It now waits for the drawing to CHANGE from
what was on screen when the button was clicked, and only then waits for it to
stop moving; if it never changes, that is recorded and the assertions say so.
Re-validated after the fix, matching the baseline exactly:
off 0/0/0 · native 37/37/0 · mst 0/0/1700 · network 0/0/23896
A repair nobody re-measured is worth nothing, and this one was wrong twice
before it was right.
The bridge candidate — the "possibly a way in" offered to a group with nothing —
was chosen from Doxa's own pins: the biggest language among the UNREACHED groups
in that country that has Scripture and the film. That is not the language a
community shares with its neighbours. For Syenara Senufo in Mali it proposed
Minianka, one people group. Mali's vehicular language is Bambara.
The number beside it was worse than the pick. "8.8% of this country's mapped
population" is a share of Doxa's unreached pins, and a reader takes a percentage
for a share of the country. A percentage measured against a denominator nobody
can see is not a caveat, it is a wrong number.
CLDR publishes what each territory speaks and by what share, joined to ISO 639-3
through SIL's own Part1 column, and pinned rather than tracked from a moving
branch. 1,161 of 1,175 candidates now come from it; the remaining 14 keep the old
proxy and say on the row that their count is of this map's groups and not of the
country. Mali now reads French at 46% and official.
Where two languages are within five points the map names BOTH and says it will
not choose. Mali is exactly that: French 46% and official, Bambara 46% and not —
one is the administrative language and the other is what people share in a
market, and which bridges better depends on whether you are handing over a
booklet or starting a conversation. A tie-break would have quietly answered a
question nobody asked it. Both carry their own links, for the same reason the
first one does.
Two field lists were a field short, the same shape twice. The candidate filter
and the link resolver both read c.s for "has a readable Scripture" and forgot
that Faith Comes By Hearing carries text on the audio record — which excluded
Bambara from Mali's candidates entirely, and then, once it was named, offered it
the film while a full 66-book text sat one field away. There is now one
predicate.
The RTL gate read a hardcoded tree path. It reads the build config instead, and
was re-run against its baseline before the result was trusted.
After the dx-maps rename, its public/doxa-maps literal made it SKIP (exit 2) without measuring.
Validated against its baseline on the renamed tree: 4/4 one-click exports, 1700x2200 PNGs,
0 errors, as before the rename. Also: usage examples and present-tense comments in the
gallery and planner name /dx-maps/ or the configured tree; both bundles rebuild byte-identical.
check:composite-freshness found stale on main: big-picture
country-posters
dashboard-plus-grid
gospel-resources
my-upg-100-list
research-map-plus-grid; also rebuilt: regions-dashboard. Rebuilt, tree + SRI
regenerated; the landing precondition (fresh, one folder, SRI) passed in main-capture scan.
Two defects, both invisible to an English diff because both sides were English.
1. THE LIST LEAF NEVER GOT THE ?lang= FIX. I repaired languageCode in
region-dashboards.vue and stopped; regions-list.vue has the same computed and the
same comment block describing the same defect, and it still read only props and
profile-config. So a shared /regions-list/?lang=ar link rendered "المناطق" as its
heading and then listed all 41 countries as "Angola", "Benin", "Cameroon".
2. OUR ARABIC WAS NOT THEIRS. Ten strings used vocabulary doxa.life does not use:
"مجموعة بشرية" where they say "مجموعة شعبية" for a people group, "مُخْدُومَة"
where they vowelise it "مَخْدُومَة", and for unengaged an invented
"غير مبشرة" / "الهدف هو القضاء على…" where they simply say "غير مَخْدُومَة".
Each replacement is a string read off their live /ar/ pages in this session, with
our placeholders re-inserted where their rendered page carried the values —
copied, not translated, which is what the brief asks for and what I can defend.
MEASURED, by the census in Arabic: 190 unaccounted additions and 8 omissions
before, 9 and 8 after the leaf fix, 0 and 0 after the vocabulary. The English
census reported a perfect clone throughout, which is exactly why the locale
dimension was worth running.
The sanctioned list now resolves through our own catalogue, so a permission
granted in English is the same permission in Arabic: "عرض ملصقات الدولة" is
recognised as the Driver's country-poster toggle instead of being reported as an
invention. Hardcoding its Arabic would only have moved the problem to zh.
Also repaired every probe URL for the dx-maps rename — 180 paths across 153 files
were still pointing at /doxa-maps/, which now 404s, so the whole measurement
apparatus was aimed at a tree that no longer exists. Replaced only the
slash-delimited segment, leaving doxa-maps-spa and the repo URL untouched, and
proved the repair by re-running the English census to its known-good baseline
before trusting anything else.
Built ONLY regions-dashboard (rule 5: studio owns the full build). This touches
library/i18n/locales/ar/regions.json, so other bundles need a consumer rebuild —
messaging studio with the sha.
It fetched the Mapbox geocoder from api.mapbox.com whatever the provider, and
never passed `provider` to the GeocoderComponent it mounts — so a partner who
chose MapLibre, the keyless engine picked precisely so that no Mapbox key and no
Mapbox traffic is involved, still sent every visitor's IP to Mapbox and still
got the Mapbox geocoder. It now resolves the provider through the usual ladder
and, under MapLibre, preloads nothing: GeocoderComponent already fetches the
MapLibre plugin itself, so the right amount of work here is none.
★ AND THIS DOES NOT FIX THE LEAK THAT WAS REPORTED, WHICH IS WORTH SAYING PLAINLY.
The audit named this file, and this file was wrong. But research-dashboard.vue:23
says in as many words that DashboardSearchHeader "remains available in the kit
but is not mounted here" — so nothing this dashboard renders was ever affected by
it. Measured on the built leaf at ?provider=maplibre, the four requests it makes
to api.mapbox.com are all tags in the GENERATED LEAF PAGE:
mapbox-gl.css · mapbox-gl.js · mapbox-gl-geocoder.css · mapbox-gl-geocoder.min.js
The leaf ships Mapbox GL and the Mapbox geocoder to every visitor regardless of
the engine asked for. That belongs to the leaf generator, not to this plugin,
and it has gone to studio as a finding.
So what lands here is a component made correct for whoever mounts it, and a gate
that names the real cause. The gate is deliberately NOT in gates:post: it cannot
pass until the leaf stops shipping those four files, and a red gate about another
lane's file teaches everyone to ignore it. Run it by name.
Two defects, both invisible to an English diff because both sides were English.
1. THE LIST LEAF NEVER GOT THE ?lang= FIX. I repaired languageCode in
region-dashboards.vue and stopped; regions-list.vue has the same computed and the
same comment block describing the same defect, and it still read only props and
profile-config. So a shared /regions-list/?lang=ar link rendered "المناطق" as its
heading and then listed all 41 countries as "Angola", "Benin", "Cameroon".
2. OUR ARABIC WAS NOT THEIRS. Ten strings used vocabulary doxa.life does not use:
"مجموعة بشرية" where they say "مجموعة شعبية" for a people group, "مُخْدُومَة"
where they vowelise it "مَخْدُومَة", and for unengaged an invented
"غير مبشرة" / "الهدف هو القضاء على…" where they simply say "غير مَخْدُومَة".
Each replacement is a string read off their live /ar/ pages in this session, with
our placeholders re-inserted where their rendered page carried the values —
copied, not translated, which is what the brief asks for and what I can defend.
MEASURED, by the census in Arabic: 190 unaccounted additions and 8 omissions
before, 9 and 8 after the leaf fix, 0 and 0 after the vocabulary. The English
census reported a perfect clone throughout, which is exactly why the locale
dimension was worth running.
The sanctioned list now resolves through our own catalogue, so a permission
granted in English is the same permission in Arabic: "عرض ملصقات الدولة" is
recognised as the Driver's country-poster toggle instead of being reported as an
invention. Hardcoding its Arabic would only have moved the problem to zh.
Also repaired every probe URL for the dx-maps rename — 180 paths across 153 files
were still pointing at /doxa-maps/, which now 404s, so the whole measurement
apparatus was aimed at a tree that no longer exists. Replaced only the
slash-delimited segment, leaving doxa-maps-spa and the repo URL untouched, and
proved the repair by re-running the English census to its known-good baseline
before trusting anything else.
Built ONLY regions-dashboard (rule 5: studio owns the full build). This touches
library/i18n/locales/ar/regions.json, so other bundles need a consumer rebuild —
messaging studio with the sha.
A page that embeds the Research Gallery or a single map sets its language with profile-config
{"locale":"ar"} ("lang" works too) and can switch it live. With profile-config only the map's
own box mirrors for Arabic; a ?lang=ar address mirrors the whole page. The example is the
gallery, <doxa-maps-spa profile-config='{"locale":"ar"}'>, which takes its locale from the
language prop or profile-config and hands it to every tab.
SUN reached the access table and the CSV first, and the map — the surface a
mobiliser actually opens — still showed Deaf groups a panel that explained their
situation honestly and then left them with nothing to do. That is the largest
population on this map with nothing: 93 groups, 17.5 million people, and
structurally so, because a spoken bridge cannot reach a Deaf community and there
was no other kind.
Clicking Deaf Beninese now offers the SUN New Testament, the SUN Bible stories
and the language itself, and says that SUN is already being taught in Benin by
Mission Évangélique des Sourds — a named local Deaf church rather than a
resource with nobody attached to it. It is offered only where the group has
nothing, because a group with Scripture in its own sign language does not need
to learn a second one, and it says on its face that it is a bridge which has to
be learnt and closes no gap in their own language.
Carried in the pin index beside the spoken bridges rather than fetched by the
leaf, so the sheet and the access table cannot disagree about what exists. It is
one small object, not a per-language join, because SUN is one signed language
for all of them.
The doctrine scan reads it too. Those URLs live in a new top-level block that the
per-language loop cannot see, so they were added to the scan in the same change
rather than found missing later, the way the access table was.
A sanctioned entry only excused OUR string, so the other half of the same control — their "Show
Filters" beside our sanctioned "Hide Filters" (our panel is open on load) — still counted as an
omission. An entry may now carry `theirs` (+ `theirsFlags`): their string for the same control, under
the same Driver citation. It is excused only while OUR half is on the page; if our control vanishes,
their string is an omission again, so a pair can never hide a real gap.
Measured on the live research page with simpleresearch's preset (people-groups-grid): as committed,
THEIRS-ONLY 12 including "Show Filters"; with `"theirs": "^Show Filters$"` added to the Hide Filters
entry, THEIRS-ONLY 11 and "Show Filters" reported as the other half of the sanctioned pair. The preset
itself is simpleresearch's to update.
servesThisTree fingerprints manifest.json, which describes BUNDLES. MEASURED 2026-09-25 (gospel):
verify:gospel-map adopted another worktree's server and reported PASS, reading 16,867 links, 93
short of this tree — the manifests were byte-identical because the change under test was a data
asset (doxa-gospel-access.json); a planted jw.org link went undetected.
- servesThisTree(base, repo, { assets }) / requireThisTree(base, repo, label, { assets }): a gate
declares the tree-relative data files it judges, and their served bytes must equal disk too.
A mismatch or a missing file is a DIFFERENT build -> requireThisTree exits 75 NOT MEASURED,
naming the file.
- verify-gospel-map converges onto it (its per-gate copy of the check is gone): the tables are
declared in its adopt check, so a same-manifest/different-data server is not adopted and its own
server is started instead; a server you NAMED that is another tree is now NOT MEASURED (75), not
"SKIP — nothing served" (exit 0).
Negative-tested: a copy of this tree with an identical manifest and one planted link in
doxa-gospel-access.json — without assets the guard says "same build" (the incident); with assets it
refuses, naming the file; requireThisTree exits 75; this tree passes; verify:gospel-map named at the
copy exits 75.
check:composite-freshness found stale on main: dashboard-plus-grid; also rebuilt: plugin-studio. Rebuilt, tree + SRI
regenerated; the landing precondition (fresh, one folder, SRI) passed in main-capture scan.
Driver, 2026-09-25: "the Mapbox token menu is not z-index on top … it is underlapping the plugins". The
header row and the pane area were siblings with no stacking context of their own, so a menu at z-index 30
lost to plugin chrome (a filter bar at 55, a drawer at 1000). The header now stacks above the content and
the pane area is isolated (isolation:isolate), so no plugin's z-index can rise over the studio's header.
Measured in a browser with a plugin open: the Settings panel and the engine menu are the topmost element at
3 of 3 sampled points.
- Check 2 read public/ from the filesystem, so a gitignored, untracked leftover (a pre-rename public/assets/
in an older worktree) REFUSED repo's clean README-only landing. It now reads the commit being landed
(git ls-tree HEAD public/). Tested: an ignored leftover passes; a committed extra folder is still refused.
- The stale-consumer refusal now states README rule 5 (dx-shepherd, 2026-09-25): one agent owns the full
build. A change that touches library/ or anything compiled into other plugins is handed to studio to land
with the consumer rebuild; a change stale only in the author's own plugin rebuilds just that bundle.
The three chart titles, subtitles and axis units were the last inline English in
this leaf. The subtitles carry a count, so they are held whole in the catalogue
with a placeholder and the number arrives as a parameter — a sentence built by
gluing a number between two fragments cannot be translated, because word order
differs and several of these locales put the number where English does not.
The fallback interpolates too. Without that, a host that never installed i18n
would render the placeholder literally on screen, which is worse than the English
sentence the fallback exists to protect.
The leak gate could not have caught any of this. Whole-label equality is the
right unit for a fixed label and blind to a sentence carrying a number: the
catalogue holds "Across all {n} languages in the corpus." and the screen shows
"Across all 5,067 languages in the corpus." Those are never equal, so three
subtitles could have shipped untranslated in eleven locales with the gate
reporting PASS. A placeholder is now a wildcard and those keys are compared by
pattern; everything else stays equality, which is what keeps a word inside a
sentence from impersonating a label. Comparison went from 2,100 pairs to 2,244.
check:composite-freshness found SEVEN composites stale on main (the committed bundle was not what its
source builds). Cause: d49b6c80 rebuilt all 25 bundles in a worktree that had not merged the dx-maps
rename — it wrote the OLD folder and left dx-maps/ stale — and every landing involved passed its own
checks. A check BESIDE the push can be skipped; this makes it a PRECONDITION of the push.
- internal/landing-preconditions.mjs, run through .main-capture/prepush on the exact commit about to
land: (1) the working tree IS that commit, (2) public/ = README.md + the configured tree only — a build
under another tree name writes another folder and is refused, (3) check:sri, (4) every composite
rebuilds to its committed bytes.
- `main-capture scan` (the Driver's harness) runs a repo's .main-capture/prepush, when present, after its
merge scan is clean; non-zero refuses the landing. Repos without the file are unaffected.
Proven with main-capture scan: (A) main as it was, 7 stale -> REFUSED naming all seven; (B) the rebuilt
main 7c96759d -> PASS; (C) a people-groups-grid string changed and committed WITHOUT a rebuild ->
REFUSED (big-picture, dashboard-plus-grid stale); (D) a public/doxa-maps/ folder beside dx-maps/ ->
REFUSED ("extra: doxa-maps"). Scratch commits were reset, never pushed.
exp-shepherd landed engineFor(map) in useMapProvider, so the expression I was
carrying can go. Theirs is stricter than mine: it instance-tests BOTH engines
rather than inferring the second from the first.
This closes a three-step path, and each step deleted something. It began as
glFor.js copied from pplr-data-maps; became one expression delegating to
useMapProvider's isMapboxMap once I found that already existed; and is now a
plain import. Nothing engine-related is left in this plugin.
Three implementations were compared before swapping, because a substitution I
have not measured is one I am guessing at: _chk-glfor-equivalence.mjs builds the
deleted copy, my interim expression and library engineFor, and runs all three
across every engine combination — mapbox map with both globals, maplibre with
both, either alone, and the case the whole fix exists for, a MapLibre map with no
Mapbox global at all. Identical in all of them.
Built on the POST-RENAME tree: 68 files under public/dx-maps/, none under the old
public/doxa-maps/. My previous all-bundle rebuild predated that rename in this
worktree and wrote orphans; confirming the tree name before building is now part
of the sequence. composite-freshness PASS on all 10, check:sri PASS.