Commit graph

1368 commits

Author SHA1 Message Date
doxa-maps-88655c70
f3a71d3ff8 Hand the research dashboard its i18n instance, so a host's locale can reach it
Embedded with profile-config {"locale":"ar"}, this dashboard rendered entirely
in English. Not a missing catalogue and not a missing i18n install — the install
was there, one line short of being usable.

ProfileLoader is the root of this custom element, and vue-i18n's useI18n()
cannot resolve its injection at a custom-element root. So ProfileLoader takes
the i18n INSTANCE by reference through inject('appI18n') and drives its global
locale from the host's config. This entry called createAppI18n() and installed
it, but never provided it under that key, so the inject returned null and the
watchEffect that applies the locale quietly did nothing.

The sibling map entry (doxa-research-map/index.js:66) has always provided it,
and this file's own header calls itself a mirror of that one. That is how the
divergence survived: the two read as identical plumbing until you diff them.

MEASURED with check:host-locale-contract, before and after:
    research-dashboard/research-dashboard   host 0 of 1891  ->  host 75 of 1891

75, not 1891, and the remainder has the one known cause already recorded against
Big Picture: most of this dashboard's chrome is read through tStatic, which
resolves the locale from the URL and the document and cannot see a locale set at
runtime. That change is parked behind another agent's landing in the same file.
2026-09-25 13:49:00 +00:00
doxa-maps-88655c70
d2b13adaec Merge remote-tracking branch 'origin/main' into exp-shepherd/label-font
# Conflicts:
#	public/doxa-maps/country-posters/countries-map/index.html
#	public/doxa-maps/country-posters/country-poster-wall/index.html
#	public/doxa-maps/country-posters/country-posters-mfe/index.html
#	public/doxa-maps/country-posters/country-posters.js
#	public/doxa-maps/doxa-maps-spa/manifest.json
#	public/doxa-maps/doxa-research-map/doxa-research-map.js
#	public/doxa-maps/doxa-research-map/research-map/index.html
#	public/doxa-maps/doxa-research-map/research-map/maplibre/index.html
#	public/doxa-maps/index.html
#	public/doxa-maps/manifest.json
2026-09-25 13:48:31 +00:00
doxa-maps-88655c70
e94cb56b79 Map labels ask for a font the engine's glyph server has: one shared helper
library/maps/label-font.js picks a symbol layer's text-font by engine: Noto Sans
(Regular, or Bold for medium/bold) under MapLibre, which is what OpenFreeMap
serves, and exactly the caller's fonts under Mapbox. Mapbox maps render as
before.

useMapClustering hard-coded 'DIN Offc Pro Medium, Arial Unicode MS Bold', which
OpenFreeMap does not have. Under MapLibre every native cluster count requested
it and got a 404; MapLibre then drew the counts in its fallback weight instead
of the intended bold. It now uses the helper, as do upg-neighbor (replacing its
own copy), prayer-walk-planner, and key-cities' city-name labels, which named no
font and so asked for MapLibre's default (also a 404 on OpenFreeMap).

Measured by importing the real composable in a browser, 3,000 points in native
mode:
- main under MapLibre: 1 glyph 404 (DIN Offc Pro Medium,Arial Unicode MS Bold),
  and the counts drawn in the fallback weight;
- this change under MapLibre: 0 glyph 404s, text-font Noto Sans Bold, 22
  clusters with counts in bold;
- this change under Mapbox: text-font still exactly DIN Offc Pro Medium /
  Arial Unicode MS Bold.
No production map reaches native cluster counts today (Simple Research and
Country Posters force the 'mst' line mode), so this was latent in shared code.

Correction to b1e0c7fa: it said those labels "drew nothing". The screenshots here
show MapLibre falls back and draws them. The defect was the failed glyph request
on every load and the wrong weight, not missing text.

Rebuilt: country-posters, doxa-research-map, upg-neighbor, prayer-walk-planner,
key-cities.
2026-09-25 13:48:10 +00:00
doxa-maps-88655c70
544819624f browser-slot: a slot is held for at most BROWSER_MAX_HOLD seconds, and only by the wrapper
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
A probe that awaited map.once('load') with no timeout held slot 1 for 89 minutes on 3 s of CPU,
and every other browser gate on the box queued behind it and timed out as NOT MEASURED.

- The command now runs as a child under `timeout --kill-after` (default 900 s, BROWSER_MAX_HOLD);
  timeout signals the whole process group, so the chrome a probe spawned dies with it. 124/137 are
  reported as 75 NOT MEASURED, naming the command.
- fd 9 (the slot lock) is closed for the child. With exec, the command and every grandchild
  inherited it, so an orphaned process kept the slot locked after its parent exited — measured
  with the previous wrapper: the next caller got 75 after its wait.
- Proven on a private slot dir (1 slot, 5 s limit): sleep 99999 freed at 5.07 s with 75 and the next
  waiter ran at 5.58 s; a TERM-ignoring child killed at 7.1 s; after a command that leaves an orphan
  the slot is free in 261 ms; exit codes and stderr pass through unchanged.
2026-09-25 13:46:33 +00:00
doxa-maps-88655c70
2983d1c020 Merge main: regenerate the gallery index; regions-dashboard verified fresh by hash
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-25 13:32:48 +00:00
doxa-maps-88655c70
925351a758 Rebuild regions-dashboard: main was carrying a bundle its source no longer builds
check:composite-freshness named it by hash — committed 8420509dde0e, rebuilt
36560d8557d6 — so main shipped a .js that did not match the source beside it.
This is the failure the build rule exists to prevent, and it reached main anyway:
recent merges brought only other plugins' source, so deriving "what to rebuild"
from the incoming diff skipped this bundle, while something in the shared tree it
compiles against had moved underneath it.

The lesson is that the incoming diff is not a sufficient trigger. The freshness
gate compares hashes and is; it now runs before I land, not after someone else
finds it. Rebuilt on current main, tree and SRI regenerated, check:sri PASS across
every leaf, and the gate now reports regions-dashboard fresh.
2026-09-25 13:28:11 +00:00
doxa-maps-88655c70
2c83cce407 Studio and stack-demo hand the host's language down to what they mount
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
A host that embeds either composite with profile-config.locale was ignored (check:host-locale-contract:
0 Arabic characters with locale "ar", against 2121 / 2249 at ?lang=ar).

- stack-demo mounts its children by tag and already threaded tk and provider; locale now rides the
  same way to the research dashboard, and the grid (which reads cfg.locale itself) gets it only when
  the host gave one. 0 -> 358: the grid half. The dashboard half waits on its own bundle providing
  appI18n.
- plugin-studio seeded its language from localStorage / the browser and handed that to every pane.
  The ladder is now host profile-config locale/lang -> ?lang= / ?locale= -> saved choice -> browser;
  a host- or URL-given language is not persisted as the reader's. 0 -> 893 (its own chrome); the rest
  is the Research Gallery pane it opens on, which does not pass the locale on yet.
- plugin-studio's language switch rebuilt a pane's profile-config by hand without provider, tokenUrl
  and treeBase, so switching language turned a maplibre pane back into a mapbox one. Mount and
  switch now share one paneConfig().
2026-09-25 13:26:01 +00:00
doxa-maps-88655c70
aed352f7b6 Rebuild research-map-plus-grid so the committed bundle is what its source builds
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
The merge that brought main's composite work in left this bundle's committed
output behind its source; check:composite-freshness named it by hash. A stale
composite ships the code it was built with, not the code the repo shows, which
is the one failure mode a reader cannot see.
2026-09-25 13:14:03 +00:00
doxa-maps-88655c70
08b6f1a39e Integrate main into bundler-2/work
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 13:10:15 +00:00
doxa-maps-88655c70
12b66a9852 Big Picture: act on the locale the host asks for, not just carry it
A host embedding this dashboard with profile-config {"locale":"ar"} got an
entirely English page. The config was threaded correctly and reached the
children; nothing ever turned it into an i18n locale.

WHY THIS COMPOSITE AND NOT ITS SIBLING, which passes the same gate untouched.
Simple Research mounts its children BY TAG, so every child is its own custom
element with its own ProfileLoader — and ProfileLoader is the only thing in the
tree that watches `config.locale || config.lang` and drives the i18n locale from
it. Big Picture BAKES its children in (it imports the .vue files), so there is
no loader anywhere beneath it and nobody was doing that job. The entry does
provide appI18n; it was simply never told which language to use.

So this composite now does what ProfileLoader does for the other one: inject the
i18n instance and drive its locale from the host's lang/locale.

MEASURED with check:host-locale-contract, before and after:
    dashboard-plus-grid   host 0 of 2121  ->  host 32 of 2121

32, not 2121, and the remaining gap has a single known cause: most of this
dashboard's chrome is read through tStatic, which resolves the locale from the
URL and the document and cannot see a locale set at runtime. That fix lives in
library/i18n/index.js, is measured, and is parked behind another agent's landing
in the same file — the measurement also showed it must be backed by a
window-level store rather than module state, because a composite loads several
bundles and each carries its own copy of that module.
2026-09-25 13:09:57 +00:00
doxa-maps-88655c70
d2ce890005 Rebuild after taking main
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-25 12:38:51 +00:00
doxa-maps-88655c70
b0c7b52243 Take main
# Conflicts:
#	package.json
#	public/doxa-maps/index.html
2026-09-25 12:38:20 +00:00
doxa-maps-88655c70
d26e416d03 Translate the list and the table headers; correct a claim I had not checked
resources-list's chrome is translated in all eleven — the three filter tabs, the
generic resource names, the people-group count — and the table's column headers
are localised at render in data-tables. resources-map, data-tables and charts now
report zero leaks by the corrected gate; what remains is in resources-list's
data-driven rows.

A CLAIM I MADE AND HAD NOT MEASURED. The previous commit said the CSV export
"keeps the data file's English field names". It does not: Tabulator exports the
column titles it is given, so a Russian reader's CSV heads "Народ","Страна",
"Регион". I checked only after asserting the opposite. Left translated on
purpose — this is a gift for people to use and someone who exports what is on
their screen should get what was on their screen — with the stable machine
contract being data/tables/ itself, which is untouched and English.

THE GATE NEEDED A THIRD CORRECTION, and this one mattered most. Substring
matching over the rendered text reported keys that were never rendered at all:
"Language" matched inside the DATA — the people-group language "Sign Language" —
and "Region" inside a region name. No length filter separates a label from a
word sitting inside one. The unit is now an element's OWN label, and a leak is a
label that EQUALS the English value while a translation exists. A language name
can no longer impersonate a UI string.

Scripts are alphabetised per the fleet convention, so parallel landings stop
colliding on the last line of package.json.
2026-09-25 12:36:41 +00:00
doxa-maps-88655c70
d78a42f2df Integrate main into bundler-2/work
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 12:21:20 +00:00
doxa-maps-88655c70
cfdbea7f9c Host locale: gate the profile-config contract, forward ?lang= into staging embeds, stop losing gate stderr
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
- check:host-locale-contract: every leaf loaded with ONLY profile-config.locale="ar" must draw at
  least half the Arabic the same page draws at ?lang=ar; regions-list is the positive control.
  Fails on this commit for 18 leaves (bundles that never provide appI18n, and every composite).
- check:staging-lang: the staging page at ?lang=ar embeds its leaf with lang=ar and the Gospel map
  renders gospel.mode.gap in Arabic. staging-forge.js (iframe + open link) and
  staging-reference-v2.html now forward ?lang= / ?locale= via withLang(); fails on the parent.
- browser-slot.sh: `exec 9>f 2>/dev/null` redirected the whole script's stderr, and the gate
  exec'd after it, to /dev/null — FAIL lines and "not measuring" messages were being discarded.
  The redirection is now scoped to the open.
- verify-map-paint: the two regions map leaves join the default list (both paint, local and live).
- Vision card reads ?lang= / ?locale= like the rest of the library.
2026-09-25 12:18:26 +00:00
doxa-maps-88655c70
471bd05cea Stop showing English where the catalogue already holds the translation
A reader on an Arabic page saw a handful of English words among the Arabic:
the table's "People Groups" title, its count line, the Engagement column, the
Gospel Access chart title, and the side drawer's close button. Every one of them
had a correct Arabic entry sitting in the catalogue; nothing looked it up.

THE CHECK CAME FIRST, AND IT REPLACES A SWEEP THAT PASSED A BROKEN TREE.
The old method sorted every rendered string into third-party / data / chrome and
counted only chrome. That split is a guess, and it guessed wrong exactly where
the defect lived: single words like "Region", "Prayer" and "Country" are as
plausible as values as they are as labels, so they scored as data and the sweep
reported zero on a page that was visibly leaking.

check:composite-locale-leaks asks a question with an answer instead:

    the English catalogue holds this exact string as a value, AND
    this locale holds a DIFFERENT value for that key, AND
    the page still renders the English one.

No judgement about what kind of word it is — if the translation exists and the
reader is not getting it, that is the defect. The two surfaces a lookup cannot
judge (vendor map chrome, and table/facet cells carrying API values) are muted
by NAME with a stated reason, never by word, so muting one can never hide the
next real leak that happens to reuse it. The gate failed on this tree naming 9
strings before any of them were fixed.

Also threaded here: both composites now accept `lang` / `locale` in
profile-config and pass it to the children they mount, alongside `tk` and
`provider`. A partner writing {"lang":"ar"} reached nothing before — the knob
was documented and read by nobody.

The prayer facet's label in DashboardResearchFilters is now a catalogue getter
too. Measured before touching it: dashboard.config.js does define prayer_level
and FACETS spreads the config last, so that literal never rendered for this
config — it is a default for a config that omits the facet, and it stays, with
the merge order written down so the next reader need not re-derive it.

Four strings remain English with no catalogue entry at all — "Top countries",
"Select all {n}", the table dock's title, and the singular "group". They need
wording in ten languages that cannot be composed from existing entries, so they
are a DECIDE card for the Driver rather than translations invented here.
2026-09-25 12:16:59 +00:00
doxa-maps-88655c70
d78d67f197 Merge remote-tracking branch 'origin/main' into exp-shepherd/shelf-gate
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
# Conflicts:
#	package.json
2026-09-25 11:48:59 +00:00
doxa-maps-88655c70
98dbdebb03 Rebuild Prayer Walk Planner on the merged main (its label font fix, plus main's change) 2026-09-25 11:48:34 +00:00
doxa-maps-88655c70
07e2e96ec4 Take main
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
# Conflicts:
#	package.json
2026-09-25 11:44:59 +00:00
doxa-maps-88655c70
299adaa9e3 A leak gate that had to be corrected twice before it could be trusted
rega's method: a string is a LEAK when the catalogue has a different value for
the locale and the page still shows the English one — a render fault, not a
missing translation. This measures that across four leaves and four locales.

IT TOOK THREE VERSIONS AND THE FIRST TWO WERE WRONG IN INSTRUCTIVE WAYS.

One: it failed with no explanation. process.exit(1) terminates before pending
stderr flushes, so the run returned 1 with every reason line missing. A gate that
cannot say why it failed is barely better than no gate. It sets exitCode now and
lets the process end.

Two: it read the whole shadow root and reported 113 leaks, nearly all false.
"JESUS Film" and "Scripture" appear inside the explanatory paragraphs this bundle
deliberately keeps in English, and short entries like "for" match inside ordinary
sentences. Crying 113 when a handful are real is worse than silence — someone
would have gone and "fixed" strings that were already correct. Every English
prose block here declares itself dir="ltr", so the check now walks the shadow
root and skips those subtrees, and ignores catalogue entries under six
characters.

WHAT IT FINDS, now that it is trustworthy: 70 real leaks, and every one is in
resources-list. resources-map, data-tables and charts are clean in ar, zh, ru and
hi. That is exactly the debt I declared two commits ago — that leaf got its title
translated and nothing else — now measured instead of estimated.

Scope is the leaf on purpose. Six keys were reported leaking in the gallery and
the leaf is clean for all six: the gallery builds its iframe src with no lang
parameter, so the embed never learns the locale and correctly falls back to
English. Embedder defect, not a bundle one, and a check that conflated them would
send someone to translate what is already translated.
2026-09-25 11:43:16 +00:00
doxa-maps-88655c70
ff36d0dfcc Merge remote-tracking branch 'origin/main' into exp-shepherd/shelf-gate
# Conflicts:
#	package.json
#	public/doxa-maps/doxa-maps-spa/manifest.json
#	public/doxa-maps/index.html
#	public/doxa-maps/manifest.json
#	public/doxa-maps/prayer-walk-planner/prayer-walk-planner.js
#	public/doxa-maps/prayer-walk-planner/prayer-walk-planner/index.html
2026-09-25 11:36:00 +00:00
doxa-maps-88655c70
b1e0c7fa61 check:experimental-shelf: every experimental plugin, measured working, and two label fonts that 404'd
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.
2026-09-25 11:35:18 +00:00
doxa-maps-88655c70
a2be2396b9 README: the Experimental shelf, with Wall of People Groups and the Prayer Walk Planner
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
A new "Experimental shelf" section pictures the two newest shelf plugins, each
shot from its own page after it finished loading: the Wall of People Groups
(all 2,107 groups as photo cards on one zoomable prayer wall, 7fec7426) and the
Prayer Walk Planner (full screen from any embed and a sharp HD print,
30e6e569). The Research Gallery tree now lists all seven shelf plugins
(adds pplr-data-maps and wall-of-people-groups). Shots staled by 92a81ed2 and
later rebuilds are retaken.
2026-09-25 11:22:52 +00:00
doxa-maps-88655c70
4cc4a1284b The last browser scripts refuse to measure a tree or dev server that is not this checkout
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
BUG-BROWSER-GATES-CAN-MEASURE-ANOTHER-WORKTREES-TREE, the remainder. A
static scan of every internal/ script that drives a browser at a local
target found eight with no identity guard (a9550858 and 59f497eb
guarded the rest):
  built tree  check-spa-tab-order -> requireThisTree
  dev server  long-frames-probe, mobile-sheet-probe, theme-flash-probe,
              plugin-gallery-smoke, share-roundtrip-smoke (--plugin-gallery),
              smoke-studio, legend-columns-probe (only when aimed at
              /plugin-designs/; its default target is the DX-life site
              it compares against on purpose)

A dev server has no built manifest, so the new
requireDevServesThisCheckout checks the source it serves. It fetches
three library files with ?raw and compares them byte for byte with
this checkout. Another tree's dev server answers differently or 403s,
and the gate exits 75 (NOT MEASURED). Vite answers a plain fetch with
the file's text and an import-style request with `export default "…"`;
both shapes are accepted, since the first version parsed only the
second and refused this checkout's own server.

Proven: against a dev server running from 319064a1's parent and a
built tree whose manifest differs by one byte, all eight exit 75 and
name the differing file (useMapInstance.js). Against this checkout,
check-spa-tab-order and smoke-studio PASS, and plugin-gallery-smoke
gets past the guard. A re-scan finds no unguarded script left.
2026-09-25 11:10:34 +00:00
doxa-maps-88655c70
92a81ed291 Rebuild the Prayer Walk Planner against merged main and regenerate the tree
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-25 10:51:59 +00:00
doxa-maps-88655c70
6356cc3876 Merge remote-tracking branch 'origin/main' into rega/research-gallery-embed
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 10:51:41 +00:00
doxa-maps-88655c70
30e6e5699b Prayer Walk Planner: full screen from any embed, and a print proven sharp by resolution
The Driver asked for full-screen, and for "a print that stays sharp when
zoomed".

Full screen. The planner mounts the shared FullscreenButton (the library's
Fullscreen-API control, with its CSS pseudo-fullscreen fallback for iOS
Safari) as a map overlay. It full-screens the whole planner, toolbar and
map, not just the canvas. Measured from a 520px embed in the Research
Gallery on a foreign page: the planner took 1280x800 and the map grew 459
→ 739px. The label is translated (Arabic "تبديل ملء الشاشة"), because the
planner now keeps the shared vue-i18n instance on its own locale. Before,
Share and Full screen took the PAGE language even when the gallery sent
another.

Print resolution, measured, not asserted. The printed map lays out at
7.53 x 8.4 in on the letter sheet (printable width = 8.5in minus the two
12mm margins; emulateMedia alone does not apply @page margins, which
misread it once as 8.48in). Its image is 2400x2688 px, so 319 x 320 DPI.
A 1:1 crop of the zoomed print was read: small street names (Sancaktar
Yokuşu Sokağı, Baki Dede Sokağı), individual building outlines and
footpaths are crisp, because the map is rendered from vector tiles at
pixelRatio 2, not upscaled from the screen. One PDF page.

check:prayer-walk-planner gains both: full screen from a 520px embed must
take the viewport, and the printed map must measure >= 300 DPI. 10/10
PASS.
2026-09-25 10:51:21 +00:00
doxa-maps-88655c70
cd53582ea4 The shared poster wall's locale is asserted, in locales that can show a leak
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
UPG-100 carried a ❌ for a PosterWall i18n leak: useI18n() said to throw
where the wall mounts, leaving the print buttons English in every
locale on upg-100-mfe, poster-wall and country-poster-wall.

IT DOES NOT REPRODUCE. Measured on the current build, three leaves x
ro/ar/zh, tree identity checked against the served manifest first: zero
English leaks, zero page errors. ro "Listă de rugăciune / Harta afișului
/ Pachet de cartonașe", ar "قائمة الصلاة / خريطة الملصق", zh "禱告名單 /
海报地图", legend "Opțiuni de tipărire" / "خيارات الطباعة" / "列印選項".
The diagnosis does not fit either: this bundle installs i18n per element
via configureApp, and createI18n is legacy:false, so useI18n() resolves
for every descendant including the wall.

THE REAL DEFECT WAS THE MISSING ASSERTION, and it is worse than the bug
would have been: nothing in the repo checked the shared wall's RENDERED
locale, so a ❌ could sit on the scoreboard with nothing able to confirm
or refute it.

★ AND IT CANNOT BE CHECKED IN ENGLISH. The wall's guard HUMANIZES the
key when useI18n() throws - buttons.prayerList becomes "Prayer List" -
which is deliberate, so a bare host shows readable English rather than a
raw key. It also means "the button says Prayer List" is true both when
i18n worked and when it threw. An en-only check passes forever while
every other locale leaks. This gate therefore runs ONLY in non-English
locales, where the translated string differs from the humanized key and
seeing the key IS the defect.

It covers FIVE consumer leaves, not the three the report named:
key-cities and regions-dashboard mount the same shared wall and were
never checked. A gate that copies a report's sampling inherits its
blind spot.

TWO WAYS IT COULD HAVE PASSED WITHOUT CHECKING ANYTHING, both real on
the first run and both now hard skips rather than green ticks: a wall
with no cards renders no chrome, and regions-list rendered 7 cards with
ZERO wall chrome because that profile mounts the wall without the print
row. Skips are named in the output with their reason - a summary that
counts only the green ones reads as full coverage.

MUTATION TESTED by reproducing the reported condition exactly - forcing
the useI18n() guard to throw - rather than corrupting a string: the gate
goes red with 28-33 leaked strings on every measured combination, which
proves both that it detects the defect and that the defect is genuinely
absent from the unmutated build. Restored, 9 combinations PASS, 6
reported as not covered.

No source change: there was nothing to fix.
2026-09-25 10:50:46 +00:00
doxa-maps-88655c70
c77207b53a check:basemap-labels: the basemap's place names follow the reader's language on both engines
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
BUILD-LOCALIZATION-SURVIVES-THE-PROVIDER-SWAP asked for a check; the
behaviour landed in 319064a1 (library/maps/localize-labels.js) without
one. This reads the labels the map actually DRAWS, at ?lang=en and
?lang=ru on the same view, under both providers:
  Mapbox    the Standard basemap's 'place-labels' featureset (a plain
            queryRenderedFeatures cannot see the imported basemap):
            Germany→Германия, Ukraine→Украина, Egypt→Египет… must flip.
  MapLibre  each country symbol's evaluated text-field: under ru it must
            be the feature's name:ru, under en not. English there
            restores OSM's own label, so no English word is expected.
It serves this checkout on an OS-assigned port. --dir points it at
another tree for the negative control.

Negative-tested: doxa-simple-map built from 319064a1's parent (no
localize-labels) and served with --dir FAILS on both engines (Mapbox:
68 country labels, none flip; MapLibre: 0/49 in Russian). This tree
PASSES on both (5 known countries flip on Mapbox; 49/49 on MapLibre,
e.g. United Kingdom → Великобритания). A first version counted the
pre-fix Mapbox run as NOT MEASURED instead of FAIL, since it saw no
English names. "Measured" now means enough country labels were drawn.
2026-09-25 10:48:42 +00:00
doxa-maps-88655c70
0eae38b20a Rebuild Wall of People Groups on the merged main
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-25 10:35:15 +00:00
doxa-maps-88655c70
3df2478a1b Merge remote-tracking branch 'origin/main' into exp-shepherd/wall
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 10:34:43 +00:00
doxa-maps-88655c70
7fec74260f Wall of People Groups: every Doxa people group on one zoomable prayer wall joins Experimental
The Instagram-style prayer wall (2,107 DX people groups as photo cards on a d3
canvas, with a pray dialog and comments) existed only as a no-build SPA whose
components were template strings compiled in the browser. It is now a bundle:
the components are SFCs in src/components, and the root is the app's own
container without the router. The API developer panel does not ship. Pinia comes
from the package. The canvas is found by ref, not document id, which cannot see
into a shadow root. Font Awesome is registered on the document too, because
@font-face does not load from a shadow root.

Data is the live pray.doxa.life list. The uupg.doxa.life endpoint no longer
answers, so that source and every photo URL built on it are gone. The card and
both dialogs show the API's image_url instead; before this, every dialog read
"No image". The 2.6 MB CSV fallback duplicated the live API and does not ship.
Missing photos draw inline, not from via.placeholder.com. Prayers and comments
stay in localStorage under a wall-pg: prefix, and the "Print (Coming Soon)"
button, which only alerted, is gone.

Measured on a served built tree: the leaf page, a script embed on another
origin, the Research Gallery tab and a 390 px phone each load all 2,107 groups
with photos and Font Awesome, 0 page errors, 0 failed requests. Zooming in and
tapping a card's heart opens the prayer dialog with the group, its status and
its photo.
2026-09-25 10:34:39 +00:00
doxa-maps-88655c70
6a2cc89207 Rebuild against merged main and regenerate the tree
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-25 10:26:03 +00:00
doxa-maps-88655c70
1ba62ef792 Merge remote-tracking branch 'origin/main' into rega/research-gallery-embed
# Conflicts:
#	public/doxa-maps/dashboard-plus-grid/dashboard-plus-grid.js
#	public/doxa-maps/dashboard-plus-grid/dashboard-plus-grid/default/index.html
#	public/doxa-maps/dashboard-plus-grid/dashboard-plus-grid/index.html
#	public/doxa-maps/doxa-maps-spa/manifest.json
#	public/doxa-maps/index.html
#	public/doxa-maps/manifest.json
#	public/doxa-maps/prayer-walk-planner/prayer-walk-planner.js
#	public/doxa-maps/prayer-walk-planner/prayer-walk-planner/index.html
2026-09-25 10:25:01 +00:00
doxa-maps-88655c70
1d78971893 Rebuild country-posters, dashboard-plus-grid, regions-dashboard and the planner against main
check:composite (content-based since da8bc790) found three composites whose
committed bundle was not what their source builds today. Rebuilt from
their owners' current source; check:composite now reports every committed
composite is exactly what its source builds. The planner is rebuilt against
the merged library.
2026-09-25 10:24:57 +00:00
doxa-maps-88655c70
622366d4d6 Rebuild the three bundles whose output collided in the merge
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
Merging main brought its build of doxa-research-map, dashboard-plus-grid and the
SPA together with this branch's; a build artifact cannot be merged line by line,
so all three were rebuilt from the merged sources rather than hand-resolved.
SRI re-verified: every leaf's baked integrity matches the bundle it loads.
2026-09-25 10:24:48 +00:00
doxa-maps-88655c70
78efae4eae Integrate main into bundler-2/work
# Conflicts:
#	public/doxa-maps/dashboard-plus-grid/dashboard-plus-grid.js
#	public/doxa-maps/dashboard-plus-grid/dashboard-plus-grid/default/index.html
#	public/doxa-maps/dashboard-plus-grid/dashboard-plus-grid/index.html
#	public/doxa-maps/doxa-maps-spa/manifest.json
#	public/doxa-maps/doxa-research-map/doxa-research-map.js
#	public/doxa-maps/doxa-research-map/research-map/index.html
#	public/doxa-maps/doxa-research-map/research-map/maplibre/index.html
#	public/doxa-maps/index.html
#	public/doxa-maps/manifest.json
2026-09-25 10:20:57 +00:00
doxa-maps-88655c70
16629f4906 Link religion and language between the map and the grid, by the one table they already share
A reader who picks a religion family on the map now sees the grid narrow to the
same peoples, and a language selection travels the same way. Region already
linked; these two did not, because the two halves do not name a religion the
same way and nothing yet proved what the honest translation was.

The map filters religion through its legend's flat tab, keyed by the FIRST
LETTER of the religion code ('B'); the grid filters through a derived axis that
runs library/colors/religion.js forward per row ('BLA' -> 'Buddhism'). One
shared table, read in opposite directions, copied by neither half — so the wire
carries the family NAME and each side resolves it against its own data.

Two measurements changed the design on the way:

  - mapStore.filters.religions, which the map's filter panel writes, is read by
    NOTHING: the mapLayers.applyFilters() it was meant to reach is called and
    never implemented anywhere in the tree. A link routed through it would have
    set state, passed a smoke test and dimmed no pin.
  - the map's legend selections are mutually exclusive by design, so it can
    light exactly one of region/religion/language. The composite mirrors that
    rather than letting the grid hold three facets the map cannot show.

Religion links at the FAMILY and not at the code, deliberately: a map cannot
distinguish 'BLA' from 'BMA' — both dim as Buddhism — so linking at the code
would promise a precision one half cannot keep.

check:linked-vocabulary proves it by measurement rather than by reading: it
compares the total reached over the composite's wire against the total the grid
gives for the same facet on its own (83 of 2,107 both ways), and runs the MUTANT
first — the legend key 'B' instead of 'Buddhism' — failing as NO-TEETH if the
wrong mapping scores the same as the right one. Measured: mutant 0, real 83.

The CLI doc's apk:debug quote was realigned in passing; package.json had gained
app:start-page and the doc still described the older chain.
2026-09-25 10:20:27 +00:00
doxa-maps-88655c70
c73b775b6d Take main
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-25 10:19:42 +00:00
doxa-maps-88655c70
59e9695aaa Correct the record: the dead bundle was my merge, not a duplicate import
dx-shepherd's verifier traced it. Merge 52ab6089 from this worktree committed
public/doxa-maps/gospel-resources/gospel-resources.js with 21 conflict markers —
both sides concatenated, 1.13 MB — and that is what threw "Identifier Ge has
already been declared" and killed every gospel leaf.

My diagnosis last commit was wrong. I blamed a module imported twice and
re-routed the import; that change was harmless but it was not the cause. The
REBUILD that came with it is what repaired the file. A plausible cause that a fix
appears to confirm is the easiest kind of wrong answer to believe.

THE ACTUAL CAUSE IS A LANDING SCRIPT I WROTE. On a conflict it resolves the one
file it expects — public/doxa-maps/index.html — and then runs "git add -A
public", which stages every OTHER file under public/ including the ones still
holding markers. One line of convenience turning a conflict git correctly refused
to guess at into a committed artefact.

So: a check that names the real condition. It scans every tracked file for an
unresolved marker, is built from character codes so it can never trip itself, and
carries a poison arm. It runs FIRST in check:gospel-all, because there is no
point measuring anything else about a tree that is mid-merge.

Also verifies the 8 links the GRN recheck recovered, which check:gospel-all
caught as shipped-but-unverified on its very first run — psi, alk, nos, jle, lma
and three more, all now confirmed landing on their own 5fish language pages.
2026-09-25 10:19:07 +00:00
doxa-maps-88655c70
fe76c18046 Merge main: regenerate the gallery index and manifests (no bundle source came in, so no rebuild)
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
2026-09-25 10:18:35 +00:00
doxa-maps-88655c70
6976b3df1c Rebuild all 23 bundles on the merged source, regenerate tree and SRI 2026-09-25 10:17:44 +00:00
doxa-maps-88655c70
413fa30d19 isMap per profile: a map-less leaf no longer bakes, or preloads, a map engine it never uses
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
generate-tree decided a single leaf's map-ness as "its own .vue
references an engine OR anything under the BUNDLE's src/ does", so a
map-less profile inherited it from a sibling. regions-dashboard/
regions-list shows a list and no map, but src/RegionCountryMap.vue made
it isMap=true. Its leaf loaded the Mapbox engine for nothing, and since
4a6886d2 its embed snippet also preloaded it.

A leaf's map-ness now follows the RELATIVE imports reachable from its
own .vue, bounded to its bundle, so a map several imports deep
(research-dashboard's DashboardMapPanel) still counts. An explicit
boolean isMap in <profile>.meta.json wins. Composites keep their
separate rule, since they do mount their siblings.

Measured: across all 34 profiles exactly one changed (regions-list,
true -> false). 18 map profiles keep isMap and their preload; 0 non-map
snippets carry one. On a served built tree regions-list makes 0 engine
requests and 0 api.mapbox.com requests and renders its list (673
elements, no errors), where before it downloaded the 1.75 MB engine.
regions-mfe still loads the engine and draws its map. The regenerated
files are only the regions-list leaf, the two manifest copies (still
byte-identical), the staging page and the gallery; check:sri passes.
2026-09-25 10:17:33 +00:00
doxa-maps-88655c70
01cc2a6a11 Merge remote-tracking branch 'origin/main' into rega/research-gallery-embed
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 10:11:23 +00:00
doxa-maps-88655c70
bad1ae9d7e Prayer Walk Planner: one scrolling toolbar row on a phone, Draw and Print first
On a 390px phone the toolbar wrapped to three rows, 139px of an 844px
screen above the map a walker is trying to read. Below 640px it is now one
row that scrolls sideways: no label is truncated, and the two actions a
walk needs, Draw and Print, lead the row so they are always on screen.
2026-09-25 10:11:19 +00:00
doxa-maps-88655c70
da8bc790b8 check:composite compares bytes, not mtimes: rebuild each composite and diff it with the committed bundle
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
The gate compared a child's newest source mtime with the parent
bundle's mtime. A git merge or checkout rewrites sources with
checkout-time mtimes, so rega's publish of beb13da3 stopped on
"doxa-maps-spa / plugin-studio / stack-demo STALE", although a rebuild
reproduced the committed bytes exactly. Two of those parents mount
their children by tag and compile none of them in. It was also
gameable the other way: checking out the parents makes them pass.

It now rebuilds each composite into a temp dir and compares sha256
with the committed bundle. Builds are deterministic (two runs give
identical bytes), so a difference is a real source change the
committed parent predates. The new vite.config.js DOXA_OUT_ROOT
redirects that build away from public/; unset, nothing changes. A bare
sibling-profile entry in composes (gospel-resources' resources-map,
data-tables) now resolves instead of being reported unresolved.

Proven: an edit to people-groups-grid.vue with no rebuild fails
big-picture, which compiles the grid, and leaves plugin-studio and
research-map-plus-grid fresh. Touching every child and library source
with no content change still passes.

On today's main: doxa-maps-spa, plugin-studio and stack-demo (the
false alarm) are fresh. country-posters, dashboard-plus-grid and
regions-dashboard are really stale and need a rebuild. About 2 minutes
for all 10 composites; use --only to narrow.
2026-09-25 10:10:08 +00:00
doxa-maps-88655c70
e417f974e5 Merge main: artifacts resolved by rebuilding, not by merging minified text
Every conflict was generated output. The incoming source touches library/i18n/
index.js and four gospel-resources views, which reach effectively every bundle,
so all 23 are rebuilt before the tree and SRI are regenerated.
2026-09-25 10:10:03 +00:00
doxa-maps-88655c70
71ca01c542 Rebuild all 23 bundles on the merged source, regenerate the tree and SRI 2026-09-25 10:09:23 +00:00
doxa-maps-88655c70
85bd7ed1e9 README: Simple Research and the Research Gallery join the phone row; stale shots refreshed
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
simpleresearch fixed the phone toast (2221d11b): at 390x844 the "Tap a
coloured dot" prompt now sits above the Progress sheet and the legend reads in
full (checked by eye). The phone row now shows seven maps in two rows of four;
Big Picture keeps its note (desktop by design; on a phone the Research Gallery
opens on Simple Research). Gospel Resources shots retaken after its bundle was
repaired on main (57fecfd0 replaced a bundle committed with merge-conflict
markers in 52ab6089). Other stale shots refreshed from the latest builds.
2026-09-25 10:09:12 +00:00
doxa-maps-88655c70
59f497eb67 Six more browser gates measure THIS build or nothing
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
a9550858 guarded twenty gates; these six were still unguarded. Each
now exits 75 (NOT MEASURED) rather than grading another tree:
- check-legend-export-reachable, check-vision-card-numbers --url:
  requireThisTree on the URL they are given.
- check-embed-basemap-paints: it picks a RANDOM port, not an
  OS-assigned one, so if another agent holds it, its own server fails
  to start and it would measure theirs. It now checks the leaf server's
  identity, stops its own servers and exits 75. Its "a server did not
  come up" path exited 1 and now exits 75.
- check-tabs-localized, check-poster-prints read library/ from
  DOXA_REPO. Pointing that at another checkout now needs
  DOXA_REPO_MEASURE_OTHER=1 (new requireThisRepo helper).
- check-packet-render refuses a packet file inside a DIFFERENT git
  checkout (new requirePathInThisCheckout helper); a file outside any
  checkout is allowed with a note.

Proven by pointing each at a wrong build: a copy of this public/ whose
manifest differs by one byte, served on its own port, and another
worktree for the file and DOXA_REPO gates. All 8 (these 6, plus
shell-scroll and upg-map-data) exit 75. On this tree they measure:
legend-export, vision-card, upg-map-data, tabs-localized and
poster-prints PASS. packet-render gets past the guard and fails on a
non-packet file, as intended. embed-basemap measures and FAILS: "the
page does NOT paint a basemap on its own baked token". That is a
product finding, not the guard, and is reported to its owner.
2026-09-25 10:03:16 +00:00