Commit graph

1057 commits

Author SHA1 Message Date
doxa-maps-88655c70
cbbebffb95 Docs: every build line a reader copies now carries BUILD_ALLOW=1
Some checks are pending
build-gate / build (push) Waiting to run
The build refuses (exit 75) without BUILD_ALLOW=1, so a DX developer
following the tutorial, the golden path or the CLI matrix hit a refusal on
the first build. The eight runnable lines now include the flag, and the
golden path says why it exists and how to build one plugin without it.
Also: the golden path said "From doxa-maps-plugin-studio/", a retired branch
name; a clone lands in the repo root.
2026-09-25 03:07:52 +00:00
doxa-maps-88655c70
ff0be67bca README: name what is on the Experimental shelf; retake eight stale shots
Some checks are pending
build-gate / build (push) Waiting to run
The Research Gallery tree said only "every other bundle". It now lists the
five the shell actually shows today, in its own order: prayer-walk-planner,
upg-neighbor, all-people-groups, country-posters, doxa-vision-card
(22 bundles - 7 curated tabs - 10 in NOT_EXPERIMENTAL).

Eight screenshots were shot from bundles that have since been rebuilt;
retaken from the current build and checked by eye.
2026-09-25 03:05:09 +00:00
doxa-maps-88655c70
2087074e9d Stop telling readers the audio sweep is unfinished — it finished
Some checks are pending
build-gate / build (push) Waiting to run
The legend's caveat is the paragraph a reader uses to decide what "nothing
found" is worth. It said GRN was being swept a batch at a time and part of the
pool was still unread. That was true while the crawl ran and false the moment it
ended, and it stayed on the page through four commits that knew better. Found by
reading a screenshot, which is the only thing that could have found it.

Corrected on all three surfaces, and scoped honestly rather than upgraded: the
sweep finished for every one of the 1,192 languages in the gap, so "nothing
found" no longer rests on an unread pool — but codes that already carry a
resource were not all asked, because the sweep was spent on the gap first. Both
halves are now on the page.

A gate holds it. The DATA decides whether the claim is allowed: while codes are
genuinely undecided it passes, and it starts failing the moment they are all
decided, so it re-arms itself for the next slow pool. It does not flag a claim
rendered under a v-if on the pending count, because that form expires on its own
and is the one to copy. Its first version asked about every code on the map
instead of the gap, measured 3,202 undecided and would have let the stale
sentence stand.
2026-09-25 02:57:50 +00:00
doxa-maps-88655c70
8a3d836e44 packets: assert a people-group card actually NAMES someone
Some checks are pending
build-gate / build (push) Waiting to run
The gate counted .card elements and checked the photo/typographic split. A card
with a photo and a BLANK NAME satisfied all of it: the total held, the split
held, the page arithmetic held. Counting proves the element exists; the spec
says pages 2..n carry "the people groups, 20 per page, with photos", and a slot
that names nobody is not an entry in a prayer list.

★ MY FIRST VERSION FAILED ITS OWN MUTATION TEST, and the reason is the useful
part. It tested card.textContent. Blanking every name still left 18 characters
per card, because the typographic fallback ("no photo available") lives INSIDE
.card — so the card was not empty, it just said nothing about a people group.
The assertion passed a packet where all 95 names were gone.

Now it measures .nm, the name line the renderer writes (packet-render.js:229).
Same lesson as the nophoto count one level up, and as the consumersOf substring
test: match the thing you mean, not a container that happens to include it.

MEASURED on laos: 95 names, 0 blank, shortest "1. Aheu" — a threshold of 3 sits
far below any real entry and above an empty one.

MUTATION-TESTED against the mutation that defeated the first version: blanking
<div class="nm"> fails with "95 of 95 people-group card(s) have no NAME",
exit 1. Restored, and check:packets-all stays green across all five shapes.
2026-09-25 02:51:50 +00:00
doxa-maps-88655c70
1b08b3fcac Regenerate after merging main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 02:50:48 +00:00
doxa-maps-88655c70
c7942ae7f7 Merge commit '28b0e505b788e47e028287144bb8039c3a68fc25' into bundler-1/work
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 02:50:22 +00:00
doxa-maps-88655c70
baa5e72e5c The title and strapline sat 10px tight, because we recreated a reset as a class
Their base/_reset.scss zeroes margins with
`body,h1,h2,h3,h4,p,figure,blockquote,dl,dd { margin: 0 }` -- a bare ELEMENT
selector, specificity 0-0-1. Their `.stack > * + *` is 0-1-0, so the stack wins
and their heading gets its 10px.

We recreated that reset as class rules, `.rd-title{margin:0}` and
`.rd-summary{margin:0}`. Those are ALSO 0-1-0, because `*` contributes nothing
to specificity, so `.rd-head > * + *` and `.rd-title` tied and source order
decided it -- the later class rule won and the stack margin was discarded.

Measured on live /regions/africa, their head is a(0px), h1(10px), p(10px).
Ours was h1(0px), p(0px): the title and strapline sat flush against the back
link, 10px tight, on every region and country page. Porting the reset as the
element selector it actually is, and deleting the two class-level margin:0
declarations, restores their cascade exactly -- both now read 10px.

Found by sweeping for the defect CLASS rather than the instance: for every
`> * + *` container in our sheets, any child that is not the first and still
computes margin-top 0 has lost the same race. That turned up three candidates,
and measuring THEIR page settled the third -- their .region-goal is 0px too, so
our .rd-goal matching at 0px was never a defect. A heuristic found the
suspects; only the live measurement could tell a defect from a faithful copy.

This is the fourth of their global rules that the shadow boundary changed the
behaviour of, after box-sizing, the list bullet and prefers-reduced-motion --
but the first where the rule DID reach us and the port altered its specificity.
Copying a global element rule as a scoped class rule is not a faithful port even
when the declaration is identical.

reset-vs-live now reports 0 differences; region-vs-live 4/4, list-vs-live 21/21,
table rows 4/4, SRI passes.
2026-09-25 02:50:05 +00:00
doxa-maps-88655c70
28b0e505b7 Point the landing recipe at the rebase hazard it can create
Some checks are pending
build-gate / build (push) Waiting to run
Landing by rebase leaves the old branch tip orphaned, and reconciling it with a
-s ours merge is how this branch got one. The rule against rebasing over such a
merge is further down the file; the recipe that creates the condition now says
so where the work happens.
2026-09-25 02:50:05 +00:00
doxa-maps-88655c70
0c08dc092c Carry the pre-rebase branch tip forward
These seven commits landed on main by rebase, so the old branch tip is no longer
an ancestor of this one. Merging it with -s ours keeps the branch pushable
without a force, and records that its content is already here.
2026-09-25 02:49:42 +00:00
doxa-maps-88655c70
e3ab02844c Rebuild this bundle on top of main: it was shipping older translations
Some checks are pending
build-gate / build (push) Waiting to run
main updated six terms on the country page and rebuilt every bundle, but this
one was branched before that landed, so its Arabic and Hindi search and profile
strings were a revision behind on the site. Rebuilt after taking main, so what
the web root serves is what the source says.
2026-09-25 02:49:16 +00:00
doxa-maps-88655c70
8245ced7c8 Ship the tables the last two commits only rebuilt
The web root carries a tracked mirror of data/tables that every previous sweep
commit updated. The two commits before this one staged data/ and the scripts and
left the mirror behind, so the pin index said 1,613 groups with nothing while the
file the page actually fetches still said 1,653. The map was right in the repo
and a build behind on the site.

All fourteen mirrored tables now match their sources byte for byte.
2026-09-25 02:44:37 +00:00
doxa-maps-88655c70
43e3102f47 Check 5fish links too, and say why YouVersion stays out
The rot sampler now covers 5fish by where its redirect LANDS — a published code
goes to its numbered language page, one they do not carry is swept to /regions —
the same rule the verifier uses, with both arms proved in band. Six links a run,
ten seconds apart, because their robots.txt asks for it. That leaves 94 of 8,510
shipped links outside every re-check, down from 5,760 this morning.

YouVersion is the largest catalogue the brief names and it is now recorded as
rejected, with the measurement: nodejs.bible.com serves it without a key but its
robots.txt is Disallow: / , www.bible.com answers even robots.txt with a bot
wall, and the unauthenticated slice returns 2 English versions against the dozens
that exist — so taking it would be both discourteous and wrong. It is named in
the unchecked list on 5,413 codes instead of being silently absent, because a
reader deciding that a group has nothing is entitled to know which pools nobody
searched.
2026-09-25 02:44:37 +00:00
doxa-maps-88655c70
2253d893b7 Finish the GRN sweep: every reachable pool has now been checked
Batch fourteen closed it — 2,347 language codes decided, 1,022 carrying Words
of Life. Forty groups leave the gap: 1,653 with nothing becomes 1,613, and the
gap breakdown drops from four situations to three, because "GRN not yet swept"
no longer describes anybody. What is left is 1,429 groups where every pool this
build can reach has been searched, 183 sign languages behind key-gated sources,
and one group with no language code at all.

Two things the sweep exposed. 5fish answers 200 for any path, so its 982 links
were being verified by a status that means nothing there; they are now judged by
where the redirect LANDS — a real code goes to its numbered language page, one
they do not carry is swept to /regions — with both arms proved in band before
any link is classified. And country-gap reads a file that another script
refreshes from the pins, so running them out of order publishes the previous
build's numbers: it said 1,653 while the map said 1,613. It now refuses to write
a table that disagrees with the index the map draws.
2026-09-25 02:44:37 +00:00
doxa-maps-88655c70
4e20a98d12 Judge a bible.is link the way the verifier already does, by its API
The band shipped an hour ago compared the page against an empty shell measured
at run time. It separates cleanly today, but verify-gospel-links.mjs had already
dropped that rule for this host and recorded why: their WAF began answering HEAD
with a 403 of the same size for real and bogus filesets, and the rule agreed
that everything was dead. /api/bibles/<id> answers 200 or a 75-byte 404.

Also corrects two claims in the same file. jesusfilm.org was excluded on the
strength of the retired ?languageId= shape, not the shareUrl path it ships now;
and knowgod.com is not unverifiable — Cru's API proves those pairs, the verifier
checks them that way, and this file simply has not reproduced it yet.
2026-09-25 02:44:36 +00:00
doxa-maps-88655c70
86ca935ed9 Re-check the links nothing was re-checking: 4,685 more, in two measured bands
The rot check exempted four hosts as "never 404". Measured on the shapes the
map actually ships: jesusfilm.org does 404, and live.bible.is separates a real
page from an empty shell by better than ten times its size. That was 69% of the
corpus outside every check; it is now 13%, and the two that stay out carry the
measurement that keeps them out.
2026-09-25 02:44:36 +00:00
doxa-maps-88655c70
ccc6ae27a6 Sweep GRN batch thirteen: 133 codes from the end
170 more codes, 57 with gospel audio. 979 codes carry a Words of Life
link and 1,220 people groups whose ONLY resource in any pool is that
recording — 79.4 million people — can hear the gospel in their own
language. Tier 0 is 1,653, from 3,457 when this began.

2,215 of the gap's 2,456 codes are decided. The "not yet swept" bucket
is down to 133 groups, so one more batch ends it, and the breakdown
will then read three situations rather than four — which the sheet was
taught to handle last tick, before it could print "0 groups still
unswept" beside a claim that four situations remained.

Provenance exact: 2,079 name GRN as checked, 2,079 decided, none
decided goes unnamed. All 57 new URLs verified before shipping.
2026-09-25 02:44:36 +00:00
doxa-maps-88655c70
ac3f7a9c95 Ship the tables the last two commits only rebuilt
The web root carries a tracked mirror of data/tables that every previous sweep
commit updated. The two commits before this one staged data/ and the scripts and
left the mirror behind, so the pin index said 1,613 groups with nothing while the
file the page actually fetches still said 1,653. The map was right in the repo
and a build behind on the site.

All fourteen mirrored tables now match their sources byte for byte.
2026-09-25 02:43:29 +00:00
doxa-maps-88655c70
437f3e0092 Check 5fish links too, and say why YouVersion stays out
The rot sampler now covers 5fish by where its redirect LANDS — a published code
goes to its numbered language page, one they do not carry is swept to /regions —
the same rule the verifier uses, with both arms proved in band. Six links a run,
ten seconds apart, because their robots.txt asks for it. That leaves 94 of 8,510
shipped links outside every re-check, down from 5,760 this morning.

YouVersion is the largest catalogue the brief names and it is now recorded as
rejected, with the measurement: nodejs.bible.com serves it without a key but its
robots.txt is Disallow: / , www.bible.com answers even robots.txt with a bot
wall, and the unauthenticated slice returns 2 English versions against the dozens
that exist — so taking it would be both discourteous and wrong. It is named in
the unchecked list on 5,413 codes instead of being silently absent, because a
reader deciding that a group has nothing is entitled to know which pools nobody
searched.
2026-09-25 02:42:53 +00:00
doxa-maps-88655c70
51a3abe482 packets: render the MIXED photo case, and assert the fallback says something
Some checks are pending
build-gate / build (push) Waiting to run
Every packet ever produced was all-photo (laos: 95 photos, 0 typographic) or
all-typographic (every fixture: 0 photos, N typographic). THE MIXED CASE HAD
NEVER BEEN RENDERED — and it is the realistic one. fetchPhotos already degrades
per-photo on a failed fetch, so any zone where a few images 404 puts exactly
that layout in front of a reader.

PHOTOS_EVERY=N keeps only every Nth photo, deterministic by INDEX rather than
random so the same command always yields the same packet — a fixture that
differs run to run cannot be a gate. The build stamp gains `mixedphotos`,
because a deliberately mixed packet and a partly-failed photo fetch deserve
opposite verdicts and nothing could tell them apart.

★ IT IS ALSO AN I18N SURFACE NOBODY HAD SEEN. The fallback card renders
t('no_photo'), translated in all 11 catalogs, and the gate COUNTED .nophoto
elements without ever looking inside one. A blank grey box passed every
assertion: `photos + typographic === cards` held, the page count held, nothing
was broken. Counting proves the element exists; it says nothing about whether
the reader sees a label or a hole.

MEASURED on the first mixed packet (PHOTOS_EVERY=3 over laos, 31 photos and 64
typographic): all 64 read "no photo available", 0 blank, all 64 with a real box.
In Spanish "sin fotografía disponible", in Arabic "لا توجد صورة متاحة" — 63 and
64 cards, 0 blank in both.

MUTATION-TESTED: emptying the fallback to `<div class="nophoto"></div>` fails
the gate with "63 of 63 typographic card(s) render an EMPTY box", exit 1.
Restored, and check:packets-all stays green across all five shapes.

One trap worth recording: the language variable is PACKET_LANG, not LANG. The
file warns about it — process.env.LANG is the shell's POSIX locale
("en_US.UTF-8") — and I still lost a run to it, regenerating three full-photo
packets that proved nothing.
2026-09-25 02:42:04 +00:00
doxa-maps-88655c70
6b1ac8da82 Merge commit 'a4c9c0a6c7025c945042e2691784b7c1b6744d63' into bundler-1/work
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 02:37:36 +00:00
doxa-maps-88655c70
d0ae01e0c6 Honour prefers-reduced-motion: their reset never reached our shadow roots
base/_reset.scss:66-79 suppresses motion site-wide with
`@media (prefers-reduced-motion: reduce) { *, *::before, *::after {
animation-duration:.01ms!important; transition-duration:.01ms!important; ... } }`
-- a DOCUMENT-level universal selector, so it styles nothing inside a shadow
root. The same boundary that swallowed their box-sizing reset.

It matters because we had ported their animation faithfully: their
RegionDetail.vue:209 and our .rd-seg / .rd-bar__slider both carry
`transition: width 1s ease-out`. Measured with emulateMedia on the segment that
actually carries the transition, in both states, because a value that is 1s in
BOTH states is the whole claim:

  theirs  1s -> 1e-05s under reduce
  ours    1s -> 1s

So a vestibular visitor got a full one-second sliding bar from us that their own
site suppresses -- a clone difference and an accessibility regression in one.
After porting the block into .rd-root and .rl-root, ours reads 1s -> 1e-05s,
matching theirs, and the unreduced case is still 1s.

Their `html:focus-within { scroll-behavior: auto }` half is deliberately NOT
ported: a shadow root cannot style the host document's html element, and the
leaf page owns that.

Found by finishing an audit I had already called complete. The previous pass
enumerated 21 conditional rules but only actually checked the [lang] and [dir]
ones -- the four :has() rules and the @media set were reasoned about, not
measured. Of those, @media print turns out to be moot for us (it hides header,
footer and nav, which an embed does not have, plus orphans/widows on p, blocked
by the same boundary and marginal), and this one was a real defect.

list-vs-live still 21 of 21, chooser 15 of 15, parity and SRI pass.
2026-09-25 02:37:08 +00:00
doxa-maps-88655c70
c8d69aeb34 Finish the GRN sweep: every reachable pool has now been checked
Batch fourteen closed it — 2,347 language codes decided, 1,022 carrying Words
of Life. Forty groups leave the gap: 1,653 with nothing becomes 1,613, and the
gap breakdown drops from four situations to three, because "GRN not yet swept"
no longer describes anybody. What is left is 1,429 groups where every pool this
build can reach has been searched, 183 sign languages behind key-gated sources,
and one group with no language code at all.

Two things the sweep exposed. 5fish answers 200 for any path, so its 982 links
were being verified by a status that means nothing there; they are now judged by
where the redirect LANDS — a real code goes to its numbered language page, one
they do not carry is swept to /regions — with both arms proved in band before
any link is classified. And country-gap reads a file that another script
refreshes from the pins, so running them out of order publishes the previous
build's numbers: it said 1,653 while the map said 1,613. It now refuses to write
a table that disagrees with the index the map draws.
2026-09-25 02:35:03 +00:00
doxa-maps-88655c70
a4c9c0a6c7 gates: refuse to measure a tree that is not this checkout
Some checks are pending
build-gate / build (push) Waiting to run
"Answers 200" and "is serving this build" are different questions, and my two
browser gates only ever asked the first. Point either at a stale server and it
measures the OLD build and reports PASS, with nothing in the output naming which
tree it saw.

★ THAT IS NOT HYPOTHETICAL — I DID IT FOR SEVENTEEN TICKS. The published site was
many merges behind while I ran these gates against it and reported the results
as live verification. They were confident measurements of a tree that was not
mine. The country-posters defect they eventually caught was only visible once
the publish made live and local agree; before that, a stale copy would have
passed either way and I would not have known the difference.

bundler-2 found this class first (BUG-BROWSER-GATES-CAN-MEASURE-ANOTHER-WORKTREES-TREE:
port 4173 on this box was held by a different worktree, and a gate reported three
confident runs about code nobody had changed) and wrote _serves-this-tree.mjs,
which hashes the served manifest.json against the one on disk. Eight gates use
it; mine did not. This adds it to the two of mine that drive a browser.

⚠ NOT MEASURED, NOT SKIP, and the distinction carries the meaning. A SKIP reads
as "nothing to check here". This is "I could not attribute what I saw", which is
a different and louder fact. Exit 2, so a caller can tell it from a pass (0) and
from a real failure (1).

A useful side effect: these gates now refuse to measure live whenever the
publish is stale, turning the silent failure mode I lived with into a loud one
at the point of use.

VERIFIED BOTH DIRECTIONS on both gates: against this checkout they measure and
pass as before (15 width x locale combinations; 195 polys · 27/27 zones · 2107
pins on both leaves); against a host serving a different build they print NOT
MEASURED and exit 2 instead of passing.

The other 26 browser gates that lack this belong to bundler-2's chain, which
owns them — reported, not converted.
2026-09-25 02:26:03 +00:00
doxa-maps-88655c70
32a6b8476f Judge a bible.is link the way the verifier already does, by its API
The band shipped an hour ago compared the page against an empty shell measured
at run time. It separates cleanly today, but verify-gospel-links.mjs had already
dropped that rule for this host and recorded why: their WAF began answering HEAD
with a 403 of the same size for real and bogus filesets, and the rule agreed
that everything was dead. /api/bibles/<id> answers 200 or a 75-byte 404.

Also corrects two claims in the same file. jesusfilm.org was excluded on the
strength of the retired ?languageId= shape, not the shareUrl path it ships now;
and knowgod.com is not unverifiable — Cru's API proves those pairs, the verifier
checks them that way, and this file simply has not reproduced it yet.
2026-09-25 02:21:02 +00:00
doxa-maps-88655c70
1b78fe3135 Regenerate after merging main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 02:09:49 +00:00
doxa-maps-88655c70
9f9d8732cf Merge commit '7c8fc15d756c8597159becff8a48da1e3de441b2' into bundler-1/work
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 02:09:44 +00:00
doxa-maps-88655c70
a395e7bf34 Country page: six more terms the clone had and live has moved past
Ran the full-page comparison against a COUNTRY page, a surface whose localised
strings had never been compared in any language. Six more divergences, all in
regions.json and all verbatim in the clone, so all copied faithfully while the
clone lagged live. Several are subtle and none would survive a glance in a
language I do not read: ru "Загрузить еще" to "Загрузить ещё"; ru search
placeholder "по именам" to "по названиям"; hi "पूर्ण प्रोफ़ाइल" to "पूरी प्रोफ़ाइल";
hi search placeholder ending "..।" to a real ellipsis "…"; ar "الملف الشخصي الكامل"
to "الملف الكامل"; ar "إبحث" to "ابحث", an alif-hamza spelling fix.

regions.json is now clean against live on BOTH the region page and the country
page, in all seven upstream locales.

Two more probe defects, both of which would have produced a false report:

SUBJECT= sets an EMPTY STRING, and the probe read process.env.SUBJECT ||
'africa'. Empty is falsy, so it silently re-ran the Africa region page while I
believed I was measuring the list page -- the leaf count gave it away at 262,
identical to the region run. Now ??, which respects an empty string.

And the list page turns out not to be comparable by leaf index at all. Their
region cards are sorted by LOCALISED name, so leaf i on /en is a different
region's card than leaf i on /ru, and aligning by index would have paired the
wrong regions' text. The alignment guard caught it and skipped six locales
rather than guess, which is the behaviour I want from it. Worth recording that
ours already matches theirs exactly here: en, ru, de and ar region-card orders
are byte-identical to live, both sorted by the translated label.

check:i18n-provenance now records 41 live-sourced keys, each beside the clone
value it replaces.
2026-09-25 02:09:20 +00:00
doxa-maps-88655c70
7c8fc15d75 Rebuild every bundle so the embedBase asset-path fix actually ships
852310d0 fixed resolveAssetsBase at source but rebuilt only my-upg-100-list.
Every bundle ships its own COMPILED copy, so thirteen others were still serving
the off-by-one — the fix existed in library/ and reached almost nobody.

FOUND BY RUNNING A GATE AGAINST THE NEWLY-PUBLISHED LIVE SITE, which is only
possible now that live matches the build. check:poster-card-fits passed locally
on country-posters and FAILED live: four preview images 404ing at
/plugin-assets/assets/previews/country-*.webp — the same missing path segment,
in a bundle I had not rebuilt.

No source change here. This is the remedy the drift gate prescribes: rebuild the
consumers. Verified after: zero bundles contain the `+"/../assets"`
concatenation, SRI consistent across every leaf, and the UPG-100 map still reads
195 polys · 27/27 zones · 2107 pins.

⚠ ONE DETECTION NOTE, because it cost a detour. Grepping for `/../assets` also
matches `assetsBase:"../../assets"` — the profile config LITERAL, which is the
correct leaf-relative value the resolver consumes. Two bundles looked "still
broken" and were fine. The resolver defect is a CONCATENATION, `+"/../assets"`;
the config is a DECLARATION. Matching text rather than meaning, again.
2026-09-25 02:06:01 +00:00
doxa-maps-88655c70
98903cb18b Re-check the links nothing was re-checking: 4,685 more, in two measured bands
The rot check exempted four hosts as "never 404". Measured on the shapes the
map actually ships: jesusfilm.org does 404, and live.bible.is separates a real
page from an empty shell by better than ten times its size. That was 69% of the
corpus outside every check; it is now 13%, and the two that stay out carry the
measurement that keeps them out.
2026-09-25 02:04:27 +00:00
doxa-maps-88655c70
51178a53fb Sweep GRN batch thirteen: 133 codes from the end
170 more codes, 57 with gospel audio. 979 codes carry a Words of Life
link and 1,220 people groups whose ONLY resource in any pool is that
recording — 79.4 million people — can hear the gospel in their own
language. Tier 0 is 1,653, from 3,457 when this began.

2,215 of the gap's 2,456 codes are decided. The "not yet swept" bucket
is down to 133 groups, so one more batch ends it, and the breakdown
will then read three situations rather than four — which the sheet was
taught to handle last tick, before it could print "0 groups still
unswept" beside a claim that four situations remained.

Provenance exact: 2,079 name GRN as checked, 2,079 decided, none
decided goes unnamed. All 57 new URLs verified before shipping.
2026-09-25 01:54:55 +00:00
doxa-maps-88655c70
e5d53615e4 Regenerate after merging main 2026-09-25 01:43:53 +00:00
doxa-maps-88655c70
326fa95cd2 Merge commit '077a350257e7d3b373f3a19ab44cc1fe8a3588be' into bundler-1/work
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 01:43:49 +00:00
doxa-maps-88655c70
9cb3720c98 Rebuild both bundles: the previous push shipped a stale .js
I broke the build rule one commit ago. That merge brought
library/utils/embedBase.js -- shared bundle source -- and I ran tree and fix:sri
without rebuilding first, which is precisely the failure the rule names: the
tree regenerates and re-hashes whatever bytes are already there, so check:sri
goes green over a stale bundle and nothing complains.

It was not a precautionary rebuild either. Both owned bundles import embedBase
directly -- regions-dashboard through region-dashboards.vue and regions-list.vue,
country-posters through country-poster-wall.vue -- and it also reaches them via
the shared PosterWall.vue and ShareButton.vue. So main carried the new shared
source against both bundles' old compiled copies for one commit.

Rebuilt both, regenerated, re-hashed. check:sri passes across every leaf and the
staleness check finds every sampled source literal present in both bundles.
2026-09-25 01:43:33 +00:00
doxa-maps-88655c70
077a350257 Merge branch 'main' of https://git.nuxtbase.app/doxa-life/doxa-maps into bundler-3/work
Some checks are pending
build-gate / build (push) Waiting to run
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 01:42:45 +00:00
doxa-maps-88655c70
3eb8c037be Regenerate the tree after merging main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 01:42:23 +00:00
doxa-maps-88655c70
91d3f529f3 Merge commit '852310d05e8c0fe8e4d00610a4cbafef95c6a4e5' into bundler-1/work
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 01:42:20 +00:00
doxa-maps-88655c70
754dddd5a1 Portuguese said "Application" above the population figure
Widening the locale comparison from ten hand-picked terms to EVERY catalogue
string their page renders found two more in the clone's own catalogue, one of
them a real mistranslation we had inherited: pt Population was "Aplicação",
which means Application, and dx-life has since corrected it on live to
"População". hi Population differed by a diacritic, जनसँख्या against live's
जनसंख्या. Both were verbatim in the clone, so both were copied faithfully.

With those two fixed, regions.json now has ZERO divergences against live across
all seven upstream locales for every string that appears on their page.

The comparison maps english -> localised from their own pages rather than from
any reading of the language: their /en and /LOC pages are the same template,
measured at 262 comparable leaves with an identical tag+class sequence across
en, ru, de, es, fr, hi, pt and ar, so leaf i on one is leaf i on the other. It
refuses rather than guesses: a locale whose leaf count or shape differs is
skipped, an English string appearing twice with different localisations is
dropped as ambiguous (74 of them), only strings that exactly equal one of our
own en values are considered so page data cannot be mistaken for catalogue text,
and templates carrying placeholders are never auto-applied.

Two defects in that probe, both caught before anything shipped:

It read the page through a template literal holding code, where `\s` is not a
recognised escape, so `.replace(/\s+/g,' ')` silently became
`.replace(/s+/g,' ')` and every run of the letter s turned into a space. German
came back as "Volk gruppen" for Volksgruppen and "1+ Men chen beten" for
"1+ Menschen beten". The bug hid in Arabic, which has no Latin s, and the
earlier sweeps happened to use the doubled `/\\s+/g` form that survives a
template literal. The extractor is now a FUNCTION passed to evaluate, which
removes the whole class of escape bug rather than patching this instance.

And it was about to hand a navigation translation to a button. Their page's only
"Pray" is an <a> inside .menu-item -- the site nav -- while our regions.json
"Pray" is the pin-popup's button, which their page never renders because popups
exist only after a click. Text identity cannot tell two components apart, so nav,
header, footer and menu items are now excluded before anything is paired; our
catalogue covers the embed, not the site shell.

The 23 remaining divergences are all in library/i18n/locales/*/legend.json --
our own shared map legend, which reaches every bundle and is not part of the
clone. They are reported, not changed: it is a cross-bundle decision, and their
English merely coincides with dx-life's.
2026-09-25 01:41:57 +00:00
doxa-maps-88655c70
2fa09852ab Let the gap breakdown survive the sweep finishing
The breakdown hardcoded "Four different situations share it" and gave
every line an unconditional row. The GRN sweep has 302 codes left, so
within an hour or two that bucket empties — and the sheet would have
rendered "· 0 (0) — Global Recordings Network has not been swept for
them yet" beside a sentence insisting there were four. Two falsehoods
at once: the count, and the implication that some remain unswept.

Each line is now conditional and the opening counts the situations
that actually have rows.

Tested by simulating completion in the data the page really loads:
with every code marked swept it renders "Three different situations",
the GRN line is gone, and the real translation gap absorbs them,
1,220 → 1,531. Restored afterwards and both asset copies verified
identical and back to 311.

Worth recording from that test: the page reads public/assets/tables/,
NOT public/doxa-maps/assets/tables/. Editing the second one changes
nothing, which cost two browser runs to discover — the first
"simulation" verified the file it edited and still watched the old
number render, which looked like a broken guard rather than a wrong
path.
2026-09-25 01:37:30 +00:00
doxa-maps-88655c70
852310d05e embedBase: the asset pool is INSIDE the tree, not beside it
Some checks are pending
build-gate / build (push) Waiting to run
resolveAssetsBase computed `<treeRoot>/../assets` -- one path segment too high.
This reached the live site. MEASURED by publishing to the catalog and reverting:

  leaf  .../doxa-maps/my-upg-100-list/upg-100-list/index.html + '../../assets'
                            -> /plugin-assets/doxa-maps/assets   200
  code  root '/plugin-assets/doxa-maps' + '/../assets'
                            -> /plugin-assets/assets             404

The UPG-100 map went from "195 polys · 27/27 zones · 2107 pins" to
"⚠ boundary fetch 404 world-adm0.geojson" -- a basemap with none of our data on
it. Reverted within minutes; live is healthy.

★ THE COMMENT JUSTIFIED THE OFF-BY-ONE IN PROSE, which is how it survived. It
said the pool is "a SIBLING of the tree -- <root>/../assets -- which is why the
leaf page's generated value is '../../assets' (two up from
<tree>/<bundle>/<profile>/)". Two up from <tree>/<bundle>/<profile>/ is <tree>,
not the tree's parent. The arithmetic was wrong and the sentence made it read
deliberate. generate-tree stages assets INSIDE the tree on purpose -- "staged N
runtime asset(s) -> doxa-maps/assets", so a dropped folder is self-contained.

⚠ WHY FIFTEEN TICKS OF GREEN GATES MISSED IT, three independent reasons:
  1. INVISIBLE LOCALLY. The dev web-root has BOTH public/assets (a sibling) and
     public/doxa-maps/assets (inside), each holding the boundaries -- so the
     wrong path RESOLVES on 127.0.0.1. The catalog publishes app/doxa-maps only;
     no sibling exists there. No local run can tell the answers apart.
  2. THE BYTE GATES PASSED. check:published and check:catalog-currency both went
     GREEN on the broken publish -- 22 of 22 byte-identical, 0 stale. They prove
     the bytes match, not that the page works.
  3. NO GATE REFERENCED THE FUNCTION, while 10+ profiles across 6 bundles call
     it.

So the fix ships with check:embed-base, which is PURE ARITHMETIC -- no browser,
no network, no served tree, so it cannot SKIP itself into irrelevance. It
computes what a leaf's own '../../assets' means and asserts the resolver agrees:
the oracle is what the generated HTML already says, not an opinion about where
assets belong. It also pins the explicit ladder rungs, so a narrow fix cannot
swallow a host-supplied assetsBase.

MUTATION-TESTED: restoring the original '/../assets' fails all four tree roots
(live catalog, dev, doxa.life drop-in, deep subpath) in milliseconds.
2026-09-25 01:30:56 +00:00
doxa-maps-88655c70
379f959038 Regenerate the tree after merging main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 01:24:42 +00:00
doxa-maps-88655c70
f28958885c Merge commit '953fe7900219be2b31fcdd4a5cac506cc6816360' into bundler-1/work
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 01:24:37 +00:00
doxa-maps-88655c70
953fe79002 Merge branch 'main' of https://git.nuxtbase.app/doxa-life/doxa-maps into bundler-3/work
Some checks are pending
build-gate / build (push) Waiting to run
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 01:23:35 +00:00
doxa-maps-88655c70
360cb3d092 Merge commit 'bc368daddac0d084896a8ec76739c61ec868be32' into bundler-1/work 2026-09-25 01:23:16 +00:00
doxa-maps-88655c70
fc882f8842 Sweep GRN batch twelve: the unswept bucket is down to 311
170 more codes, 52 with gospel audio — 31%, against 43% over the first
nine batches. The sweep runs largest-population first, so the tail is
the small and rare languages where GRN is least likely to have
reached; the falling rate is the sweep working through the hard part.

922 codes carry a Words of Life link and 1,158 people groups whose
ONLY resource in any pool is that recording — 79.4 million people —
can hear the gospel in their own language. Tier 0 is 1,715, from 3,457
when this began. 2,045 of the gap's 2,456 codes are decided, 83%.

The shape keeps telling the same story: "GRN not yet swept" has fallen
842 → 660 → 484 → 311, while the real translation gap has risen 887 →
1,220. Groups are not getting worse off; they are moving from a bucket
that meant "we have not looked" into one that means "we looked
everywhere we can reach". That is the number a translator should plan
against, and it only became sayable once the two were separated.

Provenance exact: 1,910 name GRN as checked, 1,910 decided, none
decided goes unnamed. All 52 new URLs verified before shipping.
2026-09-25 01:23:07 +00:00
doxa-maps-88655c70
508518836b Every locale: take the revised terms from live, which the clone predates
Yesterday's Arabic finding generalises. dx-life has run a terminology pass on
the live site that root-dx-life does not carry, and it is not Arabic-only: 24
terms across five more locales were rendering text the site no longer uses.
Every one of them is verbatim in the clone's own common.json, so they were
copied faithfully and the clone is simply behind.

ru 9, hi 9, de 3, es 2, pt 1. The pattern is a consistent retranslation of the
domain vocabulary -- ru "Группы людей" to "Народы", de "Angenommen" to
"Adoptiert", hi "लोगों के समूह" to "जनसमूह", es "Aprobado" to "Adoptado", pt
"Aprovado" to "Adotado" -- plus longer ones like de "Engagement-Fortschritt"
becoming "Fortschritt der Arbeit vor Ort" and ru "Прогресс принятия" becoming
"Прогресс принятия ответственности".

Mapped by position and by class, never by reading the language: the five table
headers in LOGICAL order against each localised table, the three cards
heading-for-heading and sublabel-for-sublabel, the unengaged stat by its index
among the four .region-stat__label elements, and the summary line by
.color-brand-lighter. Their English page is fetched first as a control, and the
sweep refuses to compare anything if their header or stat order stops matching
what the mapping assumes.

The change verifies itself: the sweep went from 24 divergent terms to 0 across
all six locales. A wrong mapping would have moved that count the other way.

Three probe defects fixed on the way, one of which would have shipped a bug in
six languages:
- the unengaged label was read with querySelector('.region-stat__label'), which
  returns the FIRST of four. Their order is People Groups, Countries,
  Population, Unengaged people groups, so it compared our unengaged label
  against their people-groups stat: in ru the "live" value looked like "Народы"
  when it is really "Невовлечённые народы". Applying that machine-readable
  output blindly would have relabelled the unengaged stat as "Peoples"
  everywhere. It now indexes [3] and the English control asserts that order.
- the sweep omitted the summary line entirely. It surfaced only because a
  negative test happened to tamper with that key in ru and found it still
  carrying the old wording, unmeasured. It is a TEMPLATE, so it is compared with
  the numbers stripped from both sides, and reported as NOT auto-applicable --
  the %count%/%countries% placeholders have to be put back by hand, and Hindi
  orders those clauses countries-first, which is precisely why the template has
  to come from live rather than be assembled by me.
- the provenance record grew a same-line double comma, which is a legal array
  elision, so node --check passed while the Map constructor received undefined
  and the check crashed with exit 1. That exit looked exactly like a finding.
  Parseable is not correct, and a red exit deserves reading too.

check:i18n-provenance now records 33 live-sourced keys across six locales, each
beside the value the clone still carries, and a hand-translated string is still
caught: negative-tested by inventing a Russian value for an unrecorded key,
which failed by name and returned to green on restore.
2026-09-25 01:22:56 +00:00
doxa-maps-88655c70
f4595325fa Stop the charts listing GRN as both checked and unchecked
Two defects, both found by looking at the rendered page rather than
the data behind it.

The footer read "Pools CHECKED: … grn. Pools NOT checked: …, grn (not
yet swept)." GRN is swept a batch at a time, so some codes carry it as
checked and the rest as unchecked, and unioning across the corpus
printed it on both sides. To a reader that is a flat contradiction,
and it is the kind that quietly discredits every other number on the
page. It is neither fully checked nor untouched, so it now says so
once: "grn (partly — the sweep is still running)", on the checked side
only.

And three of the eleven bars — Audio, Gospel presentation,
Discipleship training — are drawn in a green the legend never named,
so a reader had no key for them at all. The legend now carries it.

Neither was visible in the markup or the numbers. The pool strings are
assembled from per-code provenance, and the legend was three entries
that all looked reasonable until you counted the colours actually on
screen against the colours explained.
2026-09-25 01:14:48 +00:00
doxa-maps-88655c70
92d236106d Re-test links we already called checked
The verification table records that a URL resolved on the day it was
checked, and nothing ever re-tests one that passed. A publisher can
retire a page and this map will go on offering it indefinitely, still
labelled "link checked". Clause L guards the freshness of the record;
it cannot notice the web moving underneath it.

This samples 153 links — 25 per host at a fixed stride, so the same
run is repeatable — and today every one of them still resolves.

THE PART WORTH KEEPING is which hosts it refuses to check. Four of the
eleven never 404: jesusfilm.org, live.bible.is and knowgod.com serve
an identical shell for any path, and fivefish.org answers a nonsense
path with a plausible geo-located page at HTTP 200. Re-checking those
by status would report perfect health forever, which is worse than not
checking them, because it would look like evidence. They are verified
by publisher API or page title in the main verifier instead.

Three impossible URLs run in band every time. If one answers 2xx the
check reports PROVED NOTHING and exits non-zero rather than passing.
That mattered immediately: the first version sampled one link per host
instead of 25 and still said PASS — a green that meant almost nothing,
caught only by reading the count rather than the verdict.
2026-09-25 01:09:08 +00:00
doxa-maps-88655c70
bc368dadda gate: a key copied into all 11 catalogs is not a translated key
Some checks are pending
build-gate / build (push) Waiting to run
check:i18n-parity asserts every locale carries every key -- and a key can pass
that perfectly while holding the English string in all eleven files, which is
what "translated" was supposed to mean. Presence and translatedness are
different properties, and only the first was measured.

Swept every catalog: exactly THREE keys are byte-identical across all locales,
out of 210 compared. Finding three rather than three hundred is what makes this
worth gating -- it is signal, not nagging.

★ THE DEFECT IT FOUND. legend.columns.upgs is the literal string "UUPGs" in all
11 catalogs, while `population` sitting beside it IN THE SAME HEADER ROW
translates normally -- Población / 人口 / Население / السكان. The legend renders
a fully localized row with one English acronym wedged into it, and prayerLaps
carries the same string.

AND THE PRODUCT HAS ALREADY RULED ON THE CONCEPT, which is what makes this a
defect rather than a preference: packet-strings.json translates the same idea in
prose in every locale -- "unreached people groups" becomes grupos étnicos no
alcanzados / 未得之民 / неохваченных народов. The legend disagrees with the packet
the legend generates. Two honest surfaces, one vocabulary, no agreement.

⚠ IT DOES NOT PROPOSE TRANSLATIONS, and that restraint is deliberate. Drafting
candidates here produced invented initialisms -- PNA, UEVG, ННГ -- assembled
from translated phrases with no source establishing any of them as the field's
term. A wrong term looks AUTHORITATIVE to a reader who cannot check it, which is
worse than an honest English acronym. The gate names what is untranslated; a
human who knows the vocabulary supplies the words.

⚠ AND IT CARRIES AN ALLOWLIST, because some keys are correctly identical: pure
interpolation formats, brand names, ISO designations. The sweep found one --
legend.progress.segment is "{label} · {count} · {pct}%", all placeholders. Each
entry needs a REASON, so the list reads as a record of decisions rather than a
silencer.

MUTATION-TESTED THREE WAYS: making ONE locale match English is correctly NOT
flagged (it requires all of them, so a genuinely translated key cannot trip it);
making a key identical in ALL eleven IS flagged; and allowlisting a real finding
moves it from the failure list to the allowed list, 2 down to 1.
2026-09-25 01:06:08 +00:00
doxa-maps-88655c70
d1bbcf024d Regenerate the tree after merging main
Some checks are pending
build-gate / build (push) Waiting to run
2026-09-25 01:03:44 +00:00
doxa-maps-88655c70
92f5eb3982 Merge commit '1465e3a7bbacfec01bf31c15259c992a84fae623' into bundler-1/work
# Conflicts:
#	public/doxa-maps/index.html
2026-09-25 01:03:39 +00:00