The table said Jing and Pana were "outside the twelve-province subset". Both provinces
are IN that file — Guangxi and Yunnan. Measured: Jing sits 8.7 km outside Guangxi's
outline and Pana 5.4 km outside Yunnan's. They are across the border, which is a fact
about one coordinate standing for a whole people, not about a missing province. I shipped
that wrong explanation twice, in the meta and in a commit message.
Sourcing China's COMPLETE 34-unit ADM1 from geoBoundaries changes neither distance by a
metre — same source geometry, 22 more provinces, none of them theirs. So better data is
not the answer here, the downloaded file is deleted rather than staged for nothing, and
the finding is written down so nobody repeats the experiment.
A point within 15 km of exactly ONE province now joins it, carries near_border_km on its
row, and is counted in meta as placed by nearest province. Two provinces inside the
tolerance leave it unassigned: a near-miss is a hint, and a hint between two answers is
not one. 1,007 assigned becomes 1,009 and unassigned 2 becomes 0 — with the two
exceptions visible per row rather than dissolved into the total, because gospel-internet
reads this file and should be able to tell a point inside a province from one 9 km out.
f728831d switched the interactive maps to the web-sized boundaries — 232 KB against
1.84 MB, which matters on a leaf built for thousands of embeds — and this builder kept
reading the full-precision file. The map would have drawn provinces from one outline
while the table counted them from another. My own staged-assets gate caught it on the
merge, which is exactly the drift it was added for a day ago.
Measured before following the map rather than after: same 36 states, same names, 0 groups
move province, 0 dropped, and 2 GAINED. Great Andamanese and the Sentinelese had been
listed as unassigned because their points fell just outside the detailed coastline of
islands that ARE in the file; on the simplified outline both land inside Andaman and
Nicobar. 1,005 assigned becomes 1,007 and unassigned 4 becomes 2 — only Jing and Pana on
China's Vietnam and Laos borders, outside its twelve-province subset.
A coarser outline is not a worse answer here. It is a more forgiving one at the scale a
single coordinate for a whole people is honest to.
The note that called those two a precision artefact said "Four remain" and named them; it
would have been false the moment this landed, so it says what is true now and records what
the switch measured.
check:composite-freshness found stale on main: dashboard-plus-grid
doxa-research-map
research-dashboard. Rebuilt, tree + SRI
regenerated; the landing precondition (fresh, one folder, SRI) passed in main-capture scan.
check:composite-freshness found stale on main: all-people-groups
cities-internet
country-posters
dashboard-plus-grid
demo-map
doxa-research-map
doxa-simple-map
gospel-resources
key-cities
my-upg-100-list
pplr-data-maps
regions-dashboard
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.
The charts page told every reader "grn (partly — the sweep is still running)". It is
measured by check:no-stale-sweep-claim, which SKIPS rather than fails, because a code
really is undecided — and the number it skips on is ONE code out of 1,184. A reader who
sees that sentence discounts the whole audio column on the strength of 0.08%. A caveat
that is technically true and reads as "do not trust this data" is worse than a stale
number, because nothing flags it.
It states the proportion now: "grn (partly — 1 of 1,184 still to check)". The two
numbers come from the pin index, which publishes them counted exactly as the gate counts
— the codes the caveat SPEAKS FOR, tier 0 with sign languages excluded because GRN
publishes audio for them, against every code the sweep has decided. Reusing the builder's
own grnSeen set rather than rebuilding it, because a second copy could drift from the one
the tiers were computed against.
And it expires itself: at 0 pending the pool stops being described as partial at all,
which is exactly what the gate then demands instead of someone having to remember.
The classifier matched "line" as a bare word, so "LINE MAN: Food Delivery & more"
(Thailand) and "Malé Taxi Line" (Maldives) each ranked as the country's top messaging
app, ahead of WhatsApp. LINE now matches only as the whole name or before a colon.
The classifier moves to app-kind.mjs so the fetcher and the table builder share it,
and the builder re-derives each kind from the app name, so a cached fetch made under
an older rule cannot carry its answer forward. The headline count is computed from
the rows instead of typed: WhatsApp (Messenger or Business) is top in 73 countries.
Measured at ?lang=ar: the tabs render in Arabic and then the heading above the table
reads "Gospel Resources — every link, and who it reaches" in English, over English
column headers. The headers have had a lookup since cell E and it holds 13 of the 128
titles the 14 tables use; the TITLE had no lookup at all, so it came straight out of
the data file in every locale.
It is keyed by the tab's ID, not by its English text like the columns are: a title is a
sentence, and keying on it would break the day a builder rewords its own meta. The
English stays the fallback, so this renders exactly what it renders now until each key
lands.
The first version of this fix was wrong in a way worth recording: there are TWO paths —
the unfiltered table returns through localise(), the filtered one builds its own object
— and I patched the filtered path while measuring the bug on an unfiltered table. That
would have shipped a fix for the state a reader is rarely in. One resolver now, called
from both.
The exact key list is in _qa-cycle/03-deployment-cycle/KEY-REQUEST-gospel-tables.md for
studio: 14 titles and 113 headers, ordered by how many tables use each so a partial
batch still covers the most surfaces. Standards are excluded — ISO 639-3, ISO3, JSON,
CSV, ROP1 and SUN read the same in every locale.
scan REFUSED the push: country-posters, key-cities and regions-dashboard were all STALE — committed bytes
not what their source builds today.
★ AND THEY WERE STALE BECAUSE OF ME, which is the only reason this commit is mine to make. My first read
was "none of these three are my plugins", which is true and irrelevant. The chain:
my 22 joiner keys -> library/scripts/posters/packet-strings.json
-> library/components/poster/PosterWall.vue imports it
-> all three of those bundles import PosterWall (3, 1 and 1 references)
None of the three names packet-strings itself, so grepping them for it says "no" — the dependency is one
hop further out, through a shared component. A bundle's inputs are not the files it mentions; they are the
transitive closure of what it imports, which is exactly what the EVERY-bundle freshness scope exists to
catch and what "shared code changed" in scan's own output was telling me.
Same lesson as the import.meta.glob case earlier this cycle: touching one file under library/ can put
every bundle that reaches it out of date, and the only reliable way to find out is to let the gate that
rebuilds all of them say so.
fix:sri re-pinned country-posters' and regions-dashboard's leaves; key-cities needed none, which I checked
rather than assumed — a changed bundle whose leaf was NOT re-pinned is the precise shape of the mismatch
that shipped blank pages three times. Verified by recomputing sha384 across the WHOLE tree, not just my
own: 35 integrity attributes, 0 mismatches.
832.06 kB. The six public/ files the merge left on main's side are regenerated here rather than merged,
because a bundle is an output and the only correct content is what the build emits from the merged inputs.
fix:sri rewrote the leaf hashes; that summary is NOT the evidence. All three leaf integrity attributes were
re-derived here by recomputing sha384 over the emitted bundle and comparing to what each HTML claims —
3 recomputed, 0 mismatches. This is the step that was skipped when three consecutive commits shipped leaves
pinning a hash for a bundle that no longer existed: the browser refused the script and every one of those
pages was blank while the build reported success.
check:composite-freshness found stale on main: all-people-groups
cities-internet
country-posters
dashboard-plus-grid
demo-map
doxa-research-map
doxa-simple-map
gospel-resources
key-cities
my-upg-100-list
pplr-data-maps
prayer-walk-planner
regions-dashboard
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.
?codes= filters by LANGUAGE, which is what a partner's list usually is and the wrong
filter for a list of peoples. The reach table measured the gap in its own rows: its
Mandarin row counts 36 groups, those 36 speak 30 codes, and 55 Doxa groups speak those
30 — so a link from a 36-group row drew 55 pins. Akha's 33 drew 57; SUN's 93 drew 116.
The extra groups are real and also have nothing; they belong to a different row.
So the map takes ?groups=<slug list> and filters by people group. The slug is
doxa.life's own — carried from the roster into the pin rows, and read in the builder
off the prayer URL their API built from it, never constructed from a name, because a
slug this build invented could name somebody else's people. All 369 of those rows
carry a prayer URL, measured before relying on it.
Row and map now agree by construction: 0 of 35 rows disagree, and the Mandarin link
renders "36 / 2,107" on the map against a row claiming 36. The gate asserts exact
equality for a three-slug link and 0 for an unknown slug — the failure that would
quietly turn a partner's 40-group embed into the whole roster.
The longest link is 1,577 characters, SUN's 93 slugs; well inside what browsers carry,
but worth knowing before anyone adds a row with a thousand groups.
check:composite-freshness found stale on main: all-people-groups
cities-internet
country-posters
dashboard-plus-grid
demo-map
doxa-research-map
doxa-simple-map
my-upg-100-list
research-dashboard. Rebuilt, tree + SRI
regenerated; the landing precondition (fresh, one folder, SRI) passed in main-capture scan.
A row reading "36 peoples" that cannot show you which 36 is a statistic, so each row
now carries the map's own ?codes= address — the partner-embed filter doing a second
job.
It would have contradicted its own row. The map filters by LANGUAGE and these rows
count GROUPS: Mandarin's 36 groups speak 30 codes, and 55 Doxa groups speak those 30.
Akha claims 33 and the link draws 57; SUN 93 and 116. The extra groups also have
nothing — they are offered a different bridge because they are in a different country.
So both numbers ship beside the link, the column is named for languages rather than
peoples, and the meta says why. Labelling a language filter as a people filter would
have been the easy version of this and would have made the map call the table a liar.
An exact per-group filter is a change to the map's URL contract and belongs there, not
faked in a builder.
check:composite-freshness found stale on main: cities-internet
country-posters
dashboard-plus-grid
doxa-research-map
doxa-simple-map
my-upg-100-list
research-dashboard. Rebuilt, tree + SRI
regenerated; the landing precondition (fresh, one folder, SRI) passed in main-capture scan.
Every surface here answered "who has nothing", then group by group "what is the way
in for THIS one". A mobiliser with a budget asks the inverse and had nowhere to ask
it: if I resource ONE language, how many peoples does that reach? Over the 369 groups
with nothing of their own the answer is 35 rows — SUN is the way in for 93 Deaf groups
across 93 countries, Mandarin for 36, Akha for 33, Indonesian for 28. That is a
different shape of decision from a list of 369, and it is the one a sending church or
a translation agency actually makes.
The three kinds are never summed. A CURATED bridge was checked for those groups; a
CANDIDATE is computed from published CLDR data about what the country speaks, which is
a fact about the country and not about these people; SIGNED is SUN, which has to be
learned. A language offered as both a checked bridge and a computed candidate appears
once per kind, because those are different claims — which is why there are 35 rows for
32 languages.
Every row carries "still nothing of their own" and the word bridge, because
aggregating makes that easier to forget, not harder. No new link enters the map: a
bridge language's Scripture, film and training are the ones the pin index already
holds for that ISO code, and all 80 URL cells resolve to links the verification record
has already proved — measured, not assumed.
The tab carries its own English until its key lands. tr() falls back to the key, which
on a tab would read "gospel.tables.t_bridgereach" — worse than the English it replaces.
The bundle now carries the catalogue strings the two files stopped deriving from their own key names,
so it has to be regenerated: 307 modules (up from 295 — useOptionalT pulls library/i18n's tStatic and
the areas it resolves), 831.13 kB.
fix:sri rewrote the leaf hashes; that summary is NOT the evidence. All three leaf integrity attributes
were re-derived here by recomputing sha384 over the emitted bundle and comparing to what each HTML
claims — 3 recomputed, 0 mismatches. This is the step that was skipped when three consecutive commits
shipped leaves pinning a hash for a bundle that no longer existed, and the browser refused the script
on every one of those pages while the build reported success.
Three findings, one thread: rule 4 says verify a URL before shipping it, and the
check I wrote for it yesterday was measuring one file.
1. THE TABLES PAGE WAS SHIPPING DEAD FORM. people-group-resources.json — the
"Resources by People Group" tab, which a reader clicks — carried 2,294 URL cells, of
which 1,412 were jesusfilm.org/watch/?languageId=<id>. That form is IGNORED by the
site: a real id and a bogus one render the same generic library page, which is why
the map migrated onto Arclight shareUrls. The migration never reached this table, so
the booleans beside those links were being refreshed while the links themselves went
stale for a year of builds. 1,417 film and 882 scripture URLs re-derived from the
index, which is authoritative for both and whose URLs are the verified ones.
2. TWO ARCLIGHT LANGUAGES, ONE ISO CODE — resolved, with evidence rather than a
ruling. jos was SKIPPED here, and the note said resolving it meant deciding whether
the code or the publisher's name was authoritative, "a data question for someone who
can check it". Checked: media-languages?iso3=jos returns 7138 "Jordanian Sign
Language" and 139080 "Lebanese Sign Language", and 7138's shareUrl redirects to
.../jordanian-sign-language.html while a bogus arc.gt code 404s. So the code stays
authoritative for the join and the NAME breaks the tie the code cannot. The old note
also had a fact wrong — it called Lebanese "a different language (lbs)", but lbs is
LIBYAN Sign Language and Lebanese has no code of its own, so this is two Levantine
varieties sharing one code, not a mis-filing. Three Deaf groups now reach the JESUS
Film in their own sign language through a link that resolves; 0 retired-form links
remain in the index.
3. THE GATE'S SCOPE WAS THE BUG. It walked the pin index alone, which is how item 1
hid from it. It now reads every table the SURFACES read, taken from the surfaces, and
resolves each URL against four records: the verification table, the org contacts, the
SUN pages (proved by content against a catch-all control) and doxa.life's own listing
for prayer pages, which cannot be proved by fetch because pray.doxa.life answers 200
for any slug. Provenance fields are excluded by path — a citation is not an offer.
8,445 links measured yesterday; 12,292 shipped links today, 0 unrecorded, 0 broken,
0 that no status code can prove.
Reading every STAGED file instead reported 6,115 unrecorded and 53 broken, all of
them correct behaviour: API endpoints in source fields, and build caches that still
hold the 53 share URLs the verifier proved dead — which is exactly why the pin
builder drops them. A gate that goes red on correct behaviour gets switched off.
gospel-internet was running the same point-in-polygon over the same two geometry
files and refusing to write unless its per-province counts matched this table's —
which is good discipline and should not be necessary. The assignment ships now: one
row per placed group, 1,005 of them, with the language code, population and tier.
The two files are reconciled BEFORE either is written — per-province counts and the
assigned total must agree or the build refuses — because they are published
separately (the map fetches only the aggregate, lazily, and has no use for a
thousand rows) and separate files can drift. The failing direction of that guard is
not proven, only the passing one: 46 provinces agree, 1,005 = 1,005.
check:composite-freshness found stale on main: all-people-groups
big-picture
dashboard-plus-grid
my-upg-100-list
people-groups-grid
regions-dashboard
research-dashboard. Rebuilt, tree + SRI
regenerated; the landing precondition (fresh, one folder, SRI) passed in main-capture scan.
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
gospel-resources
key-cities
my-upg-100-list
people-groups-grid
pplr-data-maps
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.
The six public/ files the merge left holding main's side are regenerated here
rather than merged, because a bundle is an OUTPUT: the only correct content is
whatever `BUNDLE=my-upg-100-list vite build` emits from the merged source, and
any hand-reconciled middle version is wrong by construction even when it looks
plausible in a diff.
fix:sri then rewrote seven stale hashes across five files. That summary is NOT
the evidence: the three leaf integrity attributes were re-derived independently
here by recomputing sha384 over the emitted bundle and comparing to what each
HTML claims — 3 recomputed, 0 mismatches. This is the check that was skipped
when three consecutive commits shipped leaf HTML pinning a hash for a bundle
that no longer existed; the browser refused the script and every one of those
pages was blank, while the build's own output had reported success.
check:composite-freshness found stale on main: all-people-groups
cities-internet
country-posters
dashboard-plus-grid
demo-map
doxa-maps-spa
doxa-research-map
doxa-simple-map
gospel-resources
key-cities
my-upg-100-list
plugin-studio
pplr-data-maps
regions-dashboard
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.
The brief's third requirement at the grain the province work added: clicking a place
filters the table to it. Same split as the country and language links — a filter when
the tables are already beneath the map, a link when they are not.
The province key is a NAME, not a code: geoBoundaries' shapeName is what the table
joins on. So it is matched with diacritics folded, because a link that only works for
whoever copied it off the map is not an address — ?province=Arunachal Pradesh finds
"Arunāchal Pradesh". An unknown province matches nothing and says 0, rather than
falling back to every row.
The embed gate grew two arms for it and the first one failed for a reason that was
mine: it sliced the page text to 300 characters and then asked whether the province
name was in it. The row was on screen, 400 characters down, and the gate called the
product broken. A slice belongs in the message, never in the predicate.
Driver-found, rega-measured: under ar the five map controls mirror to the LEFT
while `left:8px` kept the legend physically left, so the legend sat underneath all
five of them. cities-internet shares the gm-legend class and passed for one reason —
it anchors inline. Every direction-dependent physical property in this stylesheet is
logical now: the legend anchor, the search clear button, the accent borders, and the
header's push-right.
Measured at 1440x900 against six controls: ar legend x=1052 with 0 overlaps, en
unchanged at x=8 with 0 overlaps.
Then the Latin rega saw. Once the explainer is folded away, exactly one sentence was
left in English, and it is the legend's only piece of NEWS — that 366 of the 369
groups with nothing have somewhere to start. It goes through tOr() with its numbers
as parameters, so it renders the English today and switches when the key lands. The
caveat trailing it ("a way in is not the gospel in their own language") moved into
the fold, where the rest of the basis lives: it qualifies the news rather than being
it. What is left in an Arabic legend outside the fold is now exactly the two strings
whose keys I have asked studio for, and nothing else.
A country is not where anyone is sent; a state is. Each province now carries the
same two prayer numbers the country sheet does and ends in the same move — the
largest group there with nothing and nobody praying, one tap from its prayer page,
ranked need-first so drilling from country to province never hands the reader a
second answer. Andhra Pradesh: 64 of its 76 groups with a prayer page have nobody
praying, 3.8M people.
Always N of M. doxa.life publishes a page for 1,723 of the roster, so nobody-praying
can be said of the groups that HAVE one and never of the province; both numbers
ship together so no surface can quote the first alone.
The join here is name+country, not language — you pray for a people in a place, and
that is doxa.life's own key. It is the one join in this build where a name match is
correct rather than forbidden, and the slug is always theirs: pray.doxa.life answers
200 for any path, so a constructed one would look valid and be empty.
The doctrine screen now scans the province table too. It is the SIXTH place a URL
lives in this bundle, and a table added after the scan was written is exactly how
the access table went unscreened for weeks — read from disk, so it is checked even
when the province layer was never opened.
This table carried its own worst case as a caveat: the ways in were chosen for the
COUNTRY, so Arunachal Pradesh — in the far north-east — was offered TELUGU, a South
Indian language, because the curated bridge table is keyed by country and a curated
bridge outranks a computed one. Manipur and Kerala got Telugu too.
The province answers for itself now. Candidates are the languages of groups
STANDING IN IT that already have Scripture AND film, ranked by the people here who
already hold them. Arunachal Pradesh gets Hindi and Assamese, Manipur gets Meitei,
Kerala gets Malayalam — each one a measured neighbour rather than a national
average. 41 of 46 provinces can answer locally; the other 5 keep the country answer
and the new Basis column says which is which, because "no language here has both
Scripture and film" is itself worth reading. The country-level answer stays in its
own column so the two can be compared rather than swapped silently.
It is a NEIGHBOUR, not a recommendation: whether a group actually speaks it is a
question for someone on the ground, and the table says so.
The fold's summary is a CONTROL — a reader in Arabic could not tell what was behind
it — and "This is a sign language." is the sentence that stops eBible's silence
being read as an absence of Scripture for 150 sign languages and 84.2M people.
Both go through tOr(), so they render today's English until exp-shepherd's keys
land and switch to the catalogue with no further change here.
check:composite-freshness found stale on main: all-people-groups
cities-internet
country-posters
dashboard-plus-grid
demo-map
doxa-research-map
doxa-simple-map
gospel-resources
key-cities
my-upg-100-list
pplr-data-maps
research-dashboard
template-bundle
wall-of-people-groups; also rebuilt: regions-dashboard big-picture upg-neighbor. Rebuilt, tree + SRI
regenerated; the landing precondition (fresh, one folder, SRI) passed in main-capture scan.
resources-map resolved its engine as `engineGlobal(provider) || window.mapboxgl`.
engineGlobal already defaults to mapbox, so that fallback could only ever fire for
provider 'maplibre' — and then it returned MAPBOX. A page that chose the keyless
engine silently got the one that needs a token, and because the profile only
assigns accessToken when the provider is not maplibre, the substitute had no token
either: it would have 401'd on every tile. The fix is the deletion. The requested
engine or a named failure, with a console warning for whoever is debugging and the
reader's line through messages.* (English kept inline until the key lands).
check:engine-contract measures it: two providers across engine present, engine
blocked and both blocked, with the CDN blocked by request routing the way a proxy
or an offline kiosk does it. Three of those six arms first failed for reasons that
were mine, not the product's, and each is written down in the gate: Mapbox cannot
mount at all on a static tree without a token, 11s was not long enough for a leaf
that fetches 4 MB before the map exists, and engine attribution read from a debug
handle scored Mapbox-with-a-bad-token as "engine null" — the canvas class cannot
lie about who drew it.
Also in this landing: the pin caveat is translated in 11 locales from the shared
pins.* area, read through this leaf's own guarded t() rather than by mounting
library ApproxPinNote.vue — that component calls useI18n() unguarded, and this
bundle is embeddable in hosts that never called app.use(i18n), where an unguarded
call throws during setup and the shadow boundary swallows it. Same wording either
way. Both "Show all groups" buttons now use the key that already exists in 11
locales, and the engine's own control labels get the provider arm.
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
gospel-resources
key-cities
my-upg-100-list
pplr-data-maps
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.
This map ships Doxa's roster, and the point of a public corpus is that other lists
can stand on it. A partner could already embed the map; their reader then had to
go hunting for their own people among 2,107 dots.
?codes=kfr,cmn,npi draw only those languages, framed on them
?country=IND open that country's sheet — this did NOTHING before unless
a ?code came with it, so the brief's second requirement had
no address, which is exactly what a partner embeds
?view=zero unchanged
The join is ISO 639-3, so a partner's list needs no agreement with ours — only the
codes their own data already carries. It filters and never invents: the notice
reads "74 / 2,107" and is itself the button back to the whole map, labelled with a
key that already exists in 11 locales. A link matching nothing shows an empty map
reading "0 / 2,107", because showing the full map instead would tell a partner
their broken list works.
check:embed-contract gates the promise. Written against the pin index first, it
demanded "12 / 12,379" of a map correctly saying "0 / 2,107" — the gate was wrong
and the map was right, so its numbers and its probe codes now come from the file
the leaf itself reads.
studio's APPROXPIN (dee9f9c9) moved library/ — legends, PinHint, useMapInstance,
i18n — so the rebuild was certain rather than conditional this time. Checked, as
always, with `git diff --name-only <base> <main> | grep '^library/'` rather than
assumed: that test was EMPTY on one earlier merge and NON-EMPTY on the next two,
and it is the only thing that decides whether the bundle must follow.
All conflicts were DERIVED artifacts again — my two leaf HTMLs and the manifests,
zero source conflicts. Resolved to main's fresh build, then regenerated from the
merged source, because picking a side of a generated file is meaningless.
VERIFIED BY HASHING, the step whose absence shipped a blank page: fix:sri rewrote
the two manifests, and I then recomputed sha384 over the actual bundle —
upg-100-list, upg-100-mfe and poster-wall all MATCH, whole-tree sweep 57 integrity
attributes with 0 mismatches. fix:sri, not tree: studio had already regenerated the
pages and the gate itself warns that tree re-reads .vue source on a shared branch.
check:composite-freshness found stale on main: all-people-groups
cities-internet
country-posters
dashboard-plus-grid
demo-map
doxa-maps-spa
doxa-research-map
doxa-simple-map
gospel-resources
key-cities
my-upg-100-list
plugin-studio
pplr-data-maps
prayer-walk-planner
regions-dashboard
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.
This leaf calls `new gl.Map` directly rather than going through useMapInstance, so
it never inherited the locale map the shared composable passes: under Arabic every
string we own was Arabic and the engine's own were not — "Toggle attribution", and
the canvas's accessible name, which is what a screen reader announces the map as.
The strings were already in the bundle; nothing was handing them to the engine.
Measured that way: the Arabic mapui catalogue is present in the built bundle both
before and after, while ENGINE_UI_KEYS — the map from the engine's key names to
ours — appears only after. Set at construction, the only time either engine reads
it. Needs eyes to confirm on screen.
The one English legend tooltip now goes through t(), with the English kept in the
template as the fallback so it renders exactly as it does today until the key
lands, rather than showing "gospel.legend.hnrTip" in a tooltip.