Commit graph

103 commits

Author SHA1 Message Date
Doxa Maps
2b70cc1aa5 Seven more references, in capitals, that a case-sensitive grep could not see
The gate matched "Driver" only, so "THE DRIVER'S ASK" and "HELD FOR THE DRIVER"
passed unseen — and so did my own verification, which used the same
case-sensitive pattern and reported zero. I checked the thing the gate checked
rather than the thing that mattered.

Seven lines across six files: five banner headings, one quoted requirement, and
the row-130 marker. Same rule as the first sweep — keep the reason, drop the
speaker — with the verbatim quote in build-doxa-gospel-access.mjs restated as
the question the map answers, since its content is the requirement and its
framing was attribution.

Comment-only: 0 non-comment lines changed, tree regenerates identically.
Case-insensitive grep across all three plugins now returns nothing.
2026-09-29 10:45:01 +00:00
Doxa Maps
6baf6dba65 Keep the reason, drop the speaker, across all three plugins
The 36 baselined references in gospel-internet (23), gospel-resources (9) and
cities-internet (4) each wrapped a real engineering reason around an attribution.
The reason is what a reader needs; the speaker is process vocabulary that does not
belong in shipped text.

Every one is rewritten in place, keeping the measurement and the date where they
were there: "NUMERALS ARE HELD fleet-wide (2026-09-26)", "(2026-09-27: one more
level of zoom-out was needed)", "Reported from use: it overlapped the bottom of
the page". Where a quote carried the requirement, the requirement is now stated
directly rather than quoted.

Comment-only: 36 insertions against 36 deletions, one line for one line, and no
built text changes. Route test passes — 0 files under library/ or internal/, so
this self-lands rather than going through studio.

Two things I nearly shipped by accident, both caught before committing:
`git add -A` swept in a 1,961-line vite.config timestamp artefact from my own
build, and editing the JSON meta through a parser reformatted "composes" from one
line to three. The artefact is deleted and the JSON is edited as a string, so the
diff is only what I meant to change.
2026-09-29 09:06:28 +00:00
Doxa Maps
b3ade5c384 Neutral wording in a table strangers read
check:public-hygiene is the white-label gate for the giveaway, and translator-org-links.json
shipped an internal role noun: the held-Bible-Society reason read "...is the Driver's ruling, not
this script's". A recipient reading our data files should not meet our process vocabulary. It now
reads "a product ruling", which keeps the meaning exactly — the point was always that this build
will not decide a doctrine question on its own.

Fixed in the script that writes the table, not the artifact, or the next build would put it back;
the same phrase in that file's header comment went too, rather than waiting to be flagged
separately now studio has closed the comment half of this class.

Regenerated by re-running the verifier: 16 of 16 organisations answered again, so the table is
current rather than hand-edited. My published tables now carry no internal vocabulary — checked
with word boundaries across all of them, which is also how I learned the six "rega" hits reported
in my sweep were the word "aggregates".
2026-09-28 00:45:42 +00:00
Doxa Maps
918bc3a28e The scope is a number in the data and a word in the surface
dx-shepherd caught what I had just shipped: "full Bible", "New Testament" and "N parts" were
being generated in the BUILDER, so English was baked into data that no locale can translate and
no catalogue owns. The same mistake I have been fixing in other people's surfaces all day, made
one commit earlier by me.

The builder now ships FCBH's part count and nothing else — 27 on 400 codes, 66 on 41, another
count on 82, none on 367 — and the leaf names it through tOr(), so it localises the moment
studio adds three keys: gospel.res.newTestament, gospel.res.fullBible and gospel.res.nParts
with {n}. English inline until then, as always.

A count that is neither 27 nor 66 is still reported as itself and never rounded up to a
Testament, and no count still names only the source.
2026-09-27 19:50:08 +00:00
Doxa Maps
f163cd6d79 Say which books, not just that a Bible exists
The Scripture chip named its source ("Faith Comes By Hearing") and stopped there, for 887
codes. FCBH publishes its own verse scope in the same record — au.vsc, "N parts, FIRST to
LAST" — and 567 of those 766 carry one: 400 New Testaments, 41 full Bibles, the rest partial.
A catalyst standing with a group needs to know whether that link is a whole Bible or two
books, and the data said so all along.

The chip now reads "Scripture · checked (New Testament — Faith Comes By Hearing)". 27 and 66
are named as Testament and Bible; anything else reports the part count FCBH claims and is never
rounded up. 367 sx codes still carry no scope — Scripture Earth's 104 and Door43's 17 publish
none in this record, and 47 FCBH rows omit it — and those say only the source, which is the
honest thing to say when the scope is unknown.

Found because go-int asked whether the FCBH text is a New Testament. It is a fair question to
ask of a map that had the answer in a field it never rendered. Their tree now reads the same
field and their green count went 128 to 195 across the roster.

Still FCBH's claim, cited, not counted book by book here — the same standard as the rest of
this corpus, and it is on the chip rather than buried in a builder.
2026-09-27 19:44:24 +00:00
Doxa Maps
37aaf69c24 rega's four notes on the card, all four mine
1 THE CHIPS SAID "unchecked" ON VERIFIED LINKS. Card mode never fetches the verification table,
so every chip understated the evidence behind it — caught on central-sama, whose Scripture and
film are both verified. Each shard now carries the states for its OWN urls, keyed by url exactly
as linkVerify is, so vchip() and vtitle() work unchanged. Shards grew 8.0 KB to 14.0 KB mean,
29 KB worst; against 6,051 KB for the map that is a price worth paying for not lying about
evidence.

2 MY GATE'S FALSE NEGATIVE, which cost rega a round: it asserted
text.includes(slug.split('-')[0]) — "abai" against a rendered "Abai Sungai". A slug fragment is
not a name. It compares the roster's name, case-folded, anywhere in the card.

3 THE EMPTY GREY BAND. The sheet is a bottom panel capped at 62% of the map; in card mode there
is no map under it, so a phone showed 285px of grey above it. Full height in card mode, and the
border radius goes with it.

4 THE CLOSE BUTTON with nothing behind it — hidden in card mode, both instances of it.

gates:pre 11/11, LINK-COVERAGE 12,292 unchanged, SFC-PARSE PASS. rega spot-checks after landing
per dx-shepherd; the render claim is still not mine to make.
2026-09-27 19:26:29 +00:00
Doxa Maps
8c99bbe611 One normalizer for the card and the map, not two that agree by hand
Third report of a blank card, and the root cause was never a missing field — it was two code
paths producing "the same" object. build-field-cards.mjs copied groupAt()'s 39 fields by hand
and missed door43, which exists only in that normalizer, so the sheet's
`!selectedGroup.door43.length` threw on undefined. I fixed a value, then a timing, then would
have added door43 and waited for the fourth.

The shard now ships the RAW INPUTS the map itself holds — the pin row, the whole code record,
the country name and the two bridge records — and the leaf's groupFrom() normalizes both
paths. groupAt(i) is a three-line wrapper over it. A field added to the sheet reaches the card
with no second edit, and forgetting one is no longer possible. The code record is passed WHOLE
rather than picked over, because picking fields is exactly the mistake above. Shards got
smaller doing it: mean 8.0 KB against 9.1.

Two sheet-level sources the card fetches nothing for are seeded from the shard: the prayer page
(prayIndex) and the SUN block. Both matter most to this persona — a catalyst with a Deaf group,
or one handing someone a prayer page — and both were silently absent while the card "worked".

Verified statically, since I cannot render: every one of the 28 fields the sheet reads off
selectedGroup is produced by groupFrom, so the two paths cannot disagree about any of them.
rega has the render.
2026-09-27 19:11:42 +00:00
Doxa Maps
3cf20a237c The Scripture chip stops saying "none found" to people who have a Bible
The sheet tested scripture_url alone, so for every code whose readable text is NOT at c.s it
rendered "Scripture · none found". 887 codes keep theirs elsewhere — on the Faith Comes By
Hearing record, on Scripture Earth, or in a Door43 translation — and 332 field cards carry
one. A catalyst reading that chip was told there is nothing to read while the link existed.

The chip now links `sx` and NAMES its source, because a reader deserves to know they are
being sent to an audio site to read: "Scripture · checked (Faith Comes By Hearing)". The
verification state and title come from the same record as any other link, so nothing here is
claimed that the link record does not already support. The card carries sx for the same
reason — the tier that promises Scripture and the panel that shows it must agree.

Two placement mistakes of mine, both caught by checking the DATA rather than the diff: the
first edit matched `credit: c.cr || null` in the bridge helper instead of the card object, and
a str.replace with no match is a silent no-op that reports success. The card had no sx and
said so when I looked for it, which is why the second attempt asserted its anchor.
2026-09-27 19:01:05 +00:00
Doxa Maps
f68cb24e78 2,001 groups had readable Scripture and were told they did not
go-int cross-checked 20 roster languages with "no New Testament found in the pools" against
open-data signals. Thirteen of the twenty had readable text all along — on a record the tier
could not see — and chasing that found one rule written four times and wrong in two of them.

THE BUG. tierOfCode tested `!!c.s`, one field, while this build's own hasReadableScripture
tested four sources. Tier 2 means "Scripture AND film", so a language whose text is on the
Faith Comes By Hearing record (au.m names the media it holds), on Scripture Earth, or in a
Door43 translation was scored 1 — "some resource found" — with its own text and the JESUS
Film both in hand. Measured: 602 codes and 2,001 of 12,379 groups move 1 -> 2. Tier 0 does
not move at all, 1,592 before and after, so nothing about who has NOTHING changes: the gap
lists, the 369 zero-access groups and the bridge-reach table are untouched.

THE SAME BUG DOWNSTREAM, and this is the one a catalyst would have felt: resourceRows typed
an FCBH record "audio, text, video" as Audio Scripture only, so the access table's
byType.Scripture never saw it. 288 people groups were published as "watch or hear, cannot
read" while their own readable Scripture sat behind that link. Access now reads 1,129 "read
and watch" (was 869), 518 "cannot read" (was 806), 85 "read" (was 57), 369 nothing found and
6 stories only — unchanged where it should be, and 2,107 in total either way. Typing the row
by its text would have dropped "can hear", so the access builder now counts a Scripture row
whose scope names audio as hearable too.

ONE OWNER NOW. hasReadableScripture is exported from gospel-resource-rows.js and imported by
the pin builder and by check-gospel-coverage-consistent, whose arm F had its own narrow copy
and therefore reported 2,001 disagreements against the corrected tier — a gate defending the
bug it exists to catch. A rule with four copies has no owner; this one has.

`sx` ships alongside: the readable text with its source named, for the 887 codes whose text is
not at c.s, so a surface can LINK what the tier now counts instead of claiming Scripture and
showing a film. Wiring it into the sheet and the field card is the next commit; this one is
data and rules only, and public/ is deliberately NOT rebuilt because rega is rendering this
worktree.
2026-09-27 18:53:52 +00:00
Doxa Maps
c9e8a3c604 The card waits for its data before it renders
Second failure on the same feature, and the cause was timing rather than values: the sheet
mounted on cardMode ALONE, so it read selected.country before the shard fetch had set it.
My previous fix put the right value in the right place and left it arriving too late.

The sheet now appears when there is DATA — v-else-if="selected || showGapList", with cardMode
removed from that condition entirely, because a card that has arrived sets both. A wait box
stands in until then and shows the error if the shard 404s, so a loading card looks like one
and a failed card says so instead of showing nothing.

And the card now carries the BRIDGE's own links. In card mode the pin index is never fetched,
so bridgeResources() found nothing and a tier-0 card would have named a way in with no way to
follow it — the single most important thing on that group's card. Derived in the builder
exactly as the leaf derives it, FCBH text fallback included, because a group's readable
Scripture is sometimes only on the audio record. Verified in the data: abai-sungai's card
carries Indonesian with its Scripture, film and Zume links.

NOT LANDING THIS until rega has rendered it on the branch. I have now shipped this feature
blank twice on checks that passed, and the missing check was always the same one: that it
renders. I cannot run a browser, so the claim is not mine to make.
2026-09-27 18:30:51 +00:00
Doxa Maps
f0798a675e A field card: one group, one fetch, a link you can send
BUILD-FIELD-RESOURCE-CARD. For the catalyst standing with the group on a phone. Seeing ONE
group used to cost the engine, the world boundaries, the basemap tiles and the 2.3 MB pin
index before a single link appeared.

?card=<slug> renders the group's sheet alone — no engine, no boundaries, no tiles, no pin
index. Measured from the served files: the map transfers 7,942 KB plus a ~700 KB engine and
unbounded tiles; the card transfers 678 KB worst case, of which 661 KB is the bundle itself
and 17 KB the largest shard. 92% fewer bytes before tiles are counted at all. The bundle is
now the floor, and splitting it is a separate job I have not done.

SHARDED BY A STABLE HASH, after two measured failures: first two letters of the slug gave a
120 KB worst shard because every "Deaf <people>" lands in "de"; three letters left 110 KB
because those slugs all begin "dea". djb2 low byte gives 255 even shards, mean 9 KB, max
17 KB, and the leaf recomputes the key so there is no index and one request. The identical
function lives in both files: if either drifts the card 404s rather than serving somebody
else's group.

Flat filenames, card-<key>.json, because internal/generate-tree.mjs stages tables/*.json as
files and throws EISDIR on a subdirectory — shared build machinery, not mine to change for
one plugin.

Share uses navigator.share where it exists (it offers whatever the catalyst already has) and
falls back to the clipboard, with a cancelled share treated as the non-event it is. The link
carries doxa.life's own slug, so what arrives reopens this card rather than a search — the
slug now reaches the sheet from a tapped pin too, so sharing works from the map as well.

Honesty unchanged: 1,738 cards carry something in the group's own language, 369 carry
nothing and say so with the way in. Checked 400 roster rows resolve to a card, 0 missing;
a tier-0 and a tier-2 card both carry what they should.
2026-09-27 17:58:05 +00:00
Doxa Maps
37edf77895 Two border peoples had no province, and my note blamed the wrong thing
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.
2026-09-27 04:23:09 +00:00
Doxa Maps
6abf71908c Count provinces from the geometry the map actually draws
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.
2026-09-27 04:19:40 +00:00
Doxa Maps
a1f3bb57bd "The sweep is still running" was true, and misleading
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.
2026-09-27 03:35:31 +00:00
Doxa Maps
60e5fdcf42 The map learned to name peoples, not just their languages
?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.
2026-09-27 02:40:12 +00:00
Doxa Maps
3883d55c7f The reach table can put those peoples on a map, and says what it shows
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.
2026-09-27 02:12:39 +00:00
Doxa Maps
c72b25e516 One language, how many peoples
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.
2026-09-27 02:09:44 +00:00
Doxa Maps
c790b39840 Every shipped link has evidence, and 1,412 of them did not
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.
2026-09-27 01:26:50 +00:00
Doxa Maps
3179c24561 Publish which province each group falls in
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.
2026-09-27 01:26:31 +00:00
Doxa Maps
376ecaee25 Prayer as a move at the grain a team is sent to
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.
2026-09-26 17:54:39 +00:00
Doxa Maps
a76eb43419 Arunachal Pradesh is no longer offered Telugu
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.
2026-09-26 17:46:32 +00:00
Doxa Maps
07644ffdbe India by state, from India's own states
The province table and the map's province layer both read gb-IND-ADM1.geojson,
which is the UPG-100 list's curated 34-unit partition — not a country's states.
Eleven groups came out with no province because they were standing in states that
partition does not draw. Both now read the complete ADM1 that landed with the
internet work (36 states, geoBoundaries via DataMeet India): 998 assigned becomes
1,005, 44 province rows become 46, and the rows name Indian states.

Four groups are still unassigned and each one is legible: Great Andamanese and the
Sentinelese sit on islands whose simplified coastline their point falls just
outside, and Jing and Pana are on China's Vietnam and Laos borders, outside its
twelve-province subset. They are listed with their coordinates, not dropped.

The staged-assets gate now also asserts the map and the table read the SAME
geometry. Staging could never catch this: both files were staged.
2026-09-26 08:37:10 +00:00
Doxa Maps
64e9fb33ef Guard the last three catalogue snapshots against a bad network day
country-languages, translation-credits and the SUN index each wrote straight to
disk. Each had a floor of its own — 150 countries, "something joined", 3 verified
pages — and a floor only catches a collapse. The run that half-succeeds clears it
and overwrites good data with less of it, which is the exact failure the guard was
written for after a rate-limited FCBH run lost a third of its video scopes.

All 16 fetchers now write through it. Re-ran two of them against the live
catalogues: 254 countries and 929 credited codes, byte-identical but for the date,
and with the floor forced high the guard refuses and leaves the file alone.
2026-09-26 07:56:31 +00:00
Doxa Maps
27bc18a6e1 Name the translator, and let a mobiliser write to them
The credit line said who translated the Scripture and called them "the likeliest
people to ask", then stopped at a name. 838 of the 929 credited groups now reach
that organisation's own homepage from the sheet.

The URL does not come from eBible's homeDomain field, which is still dropped: it
comes from a hand-screened list of 16 organisations, every one fetched on the run
that wrote the table. Where a credit names two, the one it LEADS with wins, so
"Pioneer Bible Translators and The Word for the World International" reaches
Pioneer and not the other way round.

National Bible Societies hold 89+ credits and are deliberately linked to none of
them: several UBS members publish editions carrying the deuterocanonicals, so
whether a Society contact sits inside the 66-book filter is a ruling to be made,
not one to assume. They keep the name without the link, and the count says so.

A contact is screened as somewhere to write to, not as a publisher this map reads
text from — two separate lists, so wycliffe.org appearing as a scripture link is
still a failure. Four organisations answered a datacentre IP with a bot challenge
while serving browsers normally; those are proved by robots.txt and the table
records which method proved which.
2026-09-26 07:43:58 +00:00
Doxa Maps
7e40b8aa22 Gospel access: below the border for the two countries that are half the map
P1, data layer. 855 of Doxa's 2,107 groups are in India and 154 in China — 47.9%
of everything this map draws, rendered until now as two flat fills. No other
country is close; the third is Sudan at 105. Each group is placed into a province
by point-in-polygon on its coordinate: 44 provinces with groups, 998 assigned,
13 provinces holding groups with nothing.

China's ADM1 file carries 12 provinces, not 31+, and I expected that to lose
groups. Measured instead: 152 of 154 Chinese groups fall inside them and all
twelve are used. The file was cut to the provinces where the unreached peoples
are, so the subset is the right subset.

The 11 groups that land outside every polygon are COUNTED, not dropped — nine
Indian and two Chinese, every one of them coastal or an island. That is the
predicted failure of placing a people by a single point, and it belongs in the
output rather than in a silence.

Every row is flagged approximate, because a province here comes from a coordinate
that marks where a people is found rather than a settlement, and 108 groups in
the roster share a coordinate with another group.

Going below the border also exposed something the country view was hiding:
Arunachal Pradesh, in the far north-east, is offered TELUGU as its way in,
because the curated bridge table names it and a curated bridge outranks the
computed one. India's computed default is Hindi, defensible nationally and still
wrong for much of the north-east. This table does not fix that. It records it, and
says to read the ways-in column as "a language with Scripture and film somewhere
in this country" until a bridge can be chosen per province.
2026-09-26 04:16:10 +00:00
Doxa Maps
4220a25eea Gospel access: export the bridge's training, and stop offering Deaf groups a spoken one
Two things, and the second was found by the first disagreeing with itself.

The CSV is what a team prints onto a poster or turns into a QR code, and it was
the only surface that did not say when the bridge language carries the Zume
course. 179 rows now do. Same URL the sheet already shows and the verification
already proved; nothing new is fetched.

"Candidate groups" is removed rather than renamed. It counted people groups
speaking the candidate language, which only the old pins-derived proxy could
produce; since candidates came from CLDR it has shipped as 0 on all 108 candidate
rows, and an empty column in an exported CSV reads as missing data rather than as
one that stopped applying. CLDR does give whether the language is official there,
which is a real fact about how likely it already is to be taught, so that takes
its place.

THE BUG. 182 rows had a bridge carrying Zume and only 179 could export one. The
three were Deaf groups speaking Auslan, and codes.asf has no sg flag — it is set
from the sign-language catalogues and asf is in none of them. So the pin index
built a SPOKEN bridge for them and the map offered it, because the map's is_sign
test reads that same flag. The access table refused it, because it also matches
the group name. A spoken bridge is not a weak bridge for a Deaf community, it is
not a bridge; this is the residue of the finding that already removed 89.
Suppressed on either signal now, so the two builders agree and the map cannot
offer what the table refuses.

Still 369 with nothing and still none of them without a way in.
2026-09-26 02:13:26 +00:00
Doxa Maps
9eb2557b35 Gospel research: Bible for Children checked, measured, and left out
A pool nobody here had tried, and the reason it stays out is worth writing down
so the next tick does not spend itself on the same site.

It is reachable and it 404s properly, so unlike the shells that defeated deaf
sources, its answers can be trusted. Its index publishes 118 languages with no
pagination and no stated total.

Out on coverage first: of the 294 languages in the Doxa gap, exactly ONE name
matches a folder there. 82 of the 118 match a language this index carries
somewhere, so the catalogue is real and does overlap us — just not the part with
nothing, which is the only part a new pool is worth adding for.

Out on the join key second: no ISO code is published anywhere, in the index or on
a story page. The folder IS the language name, so joining would mean matching by
name, the guess this build removed when Doxa's own API made it unnecessary, and
the guess that would have handed Akha in Laos one of three sub-varieties. The
single gap match above is a name match and worth nothing on its own.

Out on doctrine third, and that word is chosen carefully: no statement of faith
is served at any of the usual paths. This is unverified, not suspected. The brief
says leave a source out when its doctrine is unclear and say why, so that is what
this is, and it is not a finding against them.

No resource, link or count changes; the index differs only by its generated date.
2026-09-26 01:39:51 +00:00
Doxa Maps
c97876b3da Gospel access: the translator's name travels with the exported row
The sheet names who translated the Scripture, and a team printing posters or
building QR codes exports the CSV and never opens the sheet. A lead that only
exists on screen is a lead that does not reach the people doing the work, so
"Translated by" and the year are columns now: 864 of the 2,107 rows carry a named
body across 33 organisations.

Left empty rather than filled where the catalogue names a licence instead of a
translator. The column says who to ask; "public domain" answers a different
question and would read as an answer to this one.

Two pools were considered and deliberately not attributed. Faith Comes By
Hearing puts the producer inside a free-text version name — "Okpe, Delta - Bible
Society of Nigeria" has it, "Malkus Mawyon" does not — and splitting on the dash
would invent an organisation for the rows that have none; its own API, which
would answer properly, needs credentials this build does not have. Door43's URL
owner IS authoritative but is frequently a catalogue or a language namespace
rather than a body, so "translated by Door43-Catalog" would be false for 116
entries. A name nobody can act on is worse than a blank.
2026-09-25 23:41:11 +00:00
Doxa Maps
9272f1f9b7 Gospel map: name the people who translated it
The brief asks for real leads — real translators, the real organisations that do
the work — and the map had only URLs. A mobiliser who finds a neighbouring
language already has Scripture wants to know who produced it, because that
organisation has the linguists, the relationships and very often a team already
in that country. The link answers where; this answers who to ask.

eBible publishes its catalogue with a copyright line on every row and the join is
exact: 964 of the 964 eBible links in this index match a catalogue row by
translation id, none unmatched. 929 resolve to a named body across 137
organisations — Wycliffe Bible Translators, The Word for the World, Biblica,
Pioneer Bible Translators, the Seed Company, World Bible Translation Center. It
shows on the group sheet and on both bridges, where "who do I ask about this
language" is the sharpest question on the page.

NAMES ONLY, NO NEW LINKS. The catalogue also carries each publisher's domain, and
shipping it would put hosts into this map that the doctrine screen has never
seen — the screen is an allowlist precisely so a new publisher cannot arrive as a
side effect of a data refresh. Measured after the change: 16,963 links scanned,
the same count as before, and not one credit contains a URL.

A licence is not a translator. 35 rows reduce to "public domain", which says how
the text may be used and nothing about who made it; answering "who to ask" with a
legal status would be worse than saying nothing, so those are dropped and counted
separately from the rows that have no catalogue entry at all.
2026-09-25 23:38:12 +00:00
Doxa Maps
1008001ea5 Gospel map: publish a tier per language, so a language with no pin still has one
Gazan Arabs — 2,315,000 people, the second largest population this map draws —
was painted red as "nothing found" while the index held a Scripture Earth PDF and
a Zume course in South Levantine Arabic, and the access table beside it already
said "read". Because of that stale zero it was also the one group on the map
shown as having nothing AND no way in.

The cause is structural, not a typo. The tier existed only ON A PIN, and `codes`
is keyed from the resource pools while `pins` comes from the people-group
dataset, so a language can be fully researched and carry no pin at all: zero pins
speak ajp. With no tier to read, the map fell back to the roster's stored copy,
captured when that file was written and never refreshed since.

So the tier is computed for every code the index knows and published as `tiers`,
from one definition that also tiers the pins. The map reads it. It does not
re-derive a tier from the resource fields — that is the drift the surfaces gate
exists to stop, and a roster keeping its own copy is the same drift with extra
steps.

The gap is 369, not 370, and not one of them is left without a way in — so the
legend now says "every one of those 369" rather than "369 of those 369", which
read like a coincidence. Both halves still hold: every group has somewhere to
start, and none of them has the gospel in their own language.
2026-09-25 23:32:33 +00:00
Doxa Maps
229921c94f Gospel map: the Arabian peninsula gets an Arabic bridge, not Urdu
Five countries fell back to the weak pins proxy, and Saudi Arabia fell back to
URDU. The reason is a join asking for a code no publisher uses: CLDR reports
Arabic (ara) at 100% of Saudi Arabia, 81% of Oman, 74% of Libya and 100% of
Kuwait, but ara is a MACROLANGUAGE and nothing is published under it — the
Scripture and the film sit under arb. Taiwan had the same shape: zho empty, the
resources under cmn. The five countries this hit are among the least reached on
earth, which is where a wrong bridge costs the most.

Resolved through SIL's own macrolanguage membership, never by name, reusing the
table already read for the related-variety note. Every one of the 1,175
candidates now comes from published language data and none from the proxy; 211
resolve through a macrolanguage.

The share keeps the name it was measured under. CLDR measured Arabic, not
Standard Arabic, so the sheet says Arabic is spoken by 100% of this country and
that Scripture and the JESUS Film are published in Standard Arabic. Claiming the
percentage for the variety would be a figure CLDR never produced.
2026-09-25 23:24:42 +00:00
doxa-maps-88655c70
56dffd1af8 Gospel map: ask what the country speaks, not what the unreached speak
The bridge candidate — the "possibly a way in" offered to a group with nothing —
was chosen from Doxa's own pins: the biggest language among the UNREACHED groups
in that country that has Scripture and the film. That is not the language a
community shares with its neighbours. For Syenara Senufo in Mali it proposed
Minianka, one people group. Mali's vehicular language is Bambara.

The number beside it was worse than the pick. "8.8% of this country's mapped
population" is a share of Doxa's unreached pins, and a reader takes a percentage
for a share of the country. A percentage measured against a denominator nobody
can see is not a caveat, it is a wrong number.

CLDR publishes what each territory speaks and by what share, joined to ISO 639-3
through SIL's own Part1 column, and pinned rather than tracked from a moving
branch. 1,161 of 1,175 candidates now come from it; the remaining 14 keep the old
proxy and say on the row that their count is of this map's groups and not of the
country. Mali now reads French at 46% and official.

Where two languages are within five points the map names BOTH and says it will
not choose. Mali is exactly that: French 46% and official, Bambara 46% and not —
one is the administrative language and the other is what people share in a
market, and which bridges better depends on whether you are handing over a
booklet or starting a conversation. A tie-break would have quietly answered a
question nobody asked it. Both carry their own links, for the same reason the
first one does.

Two field lists were a field short, the same shape twice. The candidate filter
and the link resolver both read c.s for "has a readable Scripture" and forgot
that Faith Comes By Hearing carries text on the audio record — which excluded
Bambara from Mali's candidates entirely, and then, once it was named, offered it
the film while a full 66-book text sat one field away. There is now one
predicate.

The RTL gate read a hardcoded tree path. It reads the build config instead, and
was re-run against its baseline before the result was trusted.
2026-09-25 21:34:38 +00:00
doxa-maps-88655c70
ca548b37ba Gospel map: the Deaf groups get their way in on the map, not only in the table
SUN reached the access table and the CSV first, and the map — the surface a
mobiliser actually opens — still showed Deaf groups a panel that explained their
situation honestly and then left them with nothing to do. That is the largest
population on this map with nothing: 93 groups, 17.5 million people, and
structurally so, because a spoken bridge cannot reach a Deaf community and there
was no other kind.

Clicking Deaf Beninese now offers the SUN New Testament, the SUN Bible stories
and the language itself, and says that SUN is already being taught in Benin by
Mission Évangélique des Sourds — a named local Deaf church rather than a
resource with nobody attached to it. It is offered only where the group has
nothing, because a group with Scripture in its own sign language does not need
to learn a second one, and it says on its face that it is a bridge which has to
be learnt and closes no gap in their own language.

Carried in the pin index beside the spoken bridges rather than fetched by the
leaf, so the sheet and the access table cannot disagree about what exists. It is
one small object, not a per-language join, because SUN is one signed language
for all of them.

The doctrine scan reads it too. Those URLs live in a new top-level block that the
per-language loop cannot see, so they were added to the scan in the same change
rather than found missing later, the way the access table was.
2026-09-25 21:00:44 +00:00
doxa-maps-88655c70
cac041da42 Gospel access: a bridge for the Deaf, and screen the links nobody was screening
Of the 369 Doxa groups with nothing in their language, 94 had no bridge either,
and 93 of those were Deaf — 17.5 million people, the largest untouched number on
the map. That was structural, not bad luck: every bridge here is a spoken
language widely used in the same country, and a spoken bridge is worthless to a
Deaf community, so the build correctly refused to offer one and then had nothing
else to say.

SUN is a signed language built to be learnt across sign languages, with the Old
Testament, New Testament and Bible stories in it. It is carried the way a spoken
bridge is — something to learn, no substitute for Scripture in a group's own
sign language — in its own columns, never folded into the access rung, which
stays the honest "nothing found in their language". 28 countries where it is
already being taught come with the named local Deaf church or organisation
doing the teaching, which is the difference between a resource and a lead.

Only URLs verified by their content ship. sun.bible answers 200 for
/sun/<anything> and /resource/<anything>, so a per-language URL built by pattern
would look valid and could be empty; the fetcher re-measures that catch-all on
every run rather than trusting a note. Hosts left out are listed with the reason,
including three that are reachable by a person but not by a script.

Doctrine was checked harder here, not less. The Deaf are the most heavily
targeted group in this dataset by publishers this build must never carry, and
the thinner the pickings the stronger the pull. SUN is Wycliffe Associates'.

Screening the result exposed the bigger hole. The doctrine scan read the pin
index only, and within it skipped the Words of Life and Scripture Earth fields,
so the access table — the one the page renders and the one exported to CSV for
posters and QR codes — was never screened at all. The least supervised links in
the build were the ones most likely to be printed and handed to a stranger.
Scanning both, and both fully, takes the screened count from 6,497 to 16,960.

The gate also now proves the server holds these tables. It adopted a server from
another worktree on the default port and reported PASS on it: all three
manifests are byte-identical, because a manifest describes bundles and the
change was to a data asset it never mentions. A planted excluded-publisher link
went undetected, which is the worst possible failure for this particular check.
2026-09-25 17:47:00 +00:00
doxa-maps-88655c70
df3e383c42 One source for the bridge candidate, and the map shows it too
The table offered 107 groups a next step the map did not show. The candidate is
now computed once, in the pin build beside the curated bridge, and both surfaces
read it — the same rule that put the tier in one place. 1,167 candidates across
the research index; 107 of them reach Doxa groups the curated bridge misses.

The map's sheet renders it as its own weaker panel, and the words carry the
limit: "Possibly a way in — worth checking locally", with the language, how
many groups there speak it and what share of the country they are, and "not
ranked and not recommended". No strength, because none can honestly be
computed. Never for a sign language.

A REGRESSION I INTRODUCED AND ALMOST SHIPPED. The edit that wired this used
pinCandidates without declaring it — a string replace that matched nothing and
said nothing. The ReferenceError died inside the custom-element boundary, so the
map never mounted AND the page reported zero errors. My probe then failed with a
30-second timeout that I had caused separately, by passing playwright's timeout
as the waitForFunction ARG rather than its options, which had been silently
using the default in eleven scripts. Two faults stacked: a broken page that
looked error-free, and a probe too impatient to say so.

Zero page errors is not the same as working, and that is the only reason this
was caught: __grState never appeared, which no assertion about pins could have
explained away. Verified after the fix: 2,107 pins, gap cuts to 370, the
candidate panel renders for Abai Sungai naming Malay, and 25 Deaf sheets still
show no bridge at all.
2026-09-25 09:29:48 +00:00
doxa-maps-88655c70
8313f766b6 Bridge candidates as evidence, and no spoken bridge for a Deaf community
The curated bridge covers 168 of Doxa's 369 with nothing, because it comes from
a curated subset rather than from the data. A candidate IS computable for the
rest: the largest language in their country carrying both Scripture and the film.

WHAT I COULD NOT COMPUTE, AND THEREFORE DO NOT CLAIM. Ranking those candidates
needs a notion of "is this a lingua franca", and neither available proxy
survives contact. By GROUP COUNT, Oman picks Swahili — two groups — for an
Arabic-speaking people. By SHARE OF PINNED POPULATION, a 10% floor REFUSES Urdu
in Pakistan at 7.3%, the national language, because few Pakistani groups are
pinned as Urdu speakers. That is the same bad-denominator mistake this map
already fixed on the choropleth. So no strength is invented: the candidate ships
with its group count and its share, under column names that say "candidate",
and a reader who knows Pakistan can see Urdu at 7.3% and know what to do with it.
A score would have thrown it away on their behalf.

AND THE ERROR CAUGHT BEFORE SHIPPING. The candidate logic was offering 89 Deaf
groups a SPOKEN language — Deaf Afghans to Turkmen, Deaf Arubans to Dutch. That
is not a weak bridge, it is not a bridge, and a mobiliser acting on it would be
sent to do something that cannot reach the people. This build already refuses to
search GRN for sign languages because GRN publishes audio; the candidate logic
had inherited the gap. Now: 92 Deaf groups are offered none, and their gap is
named as the access problem it is.

Of the 369 with nothing: 168 curated bridge, 107 computed candidate, 92 Deaf,
2 with neither.
2026-09-25 08:40:25 +00:00
doxa-maps-88655c70
01d768fdb6 Put the way in next to the gap: bridge languages in the Doxa export
The access table could say "nothing found" and stop. For 168 of Doxa's 369
that is not the whole truth: a language already spoken widely in their country
has Scripture and the film. Afitti in Sudan has nothing of their own; Sudanese
Arabic, spoken by 65 groups around them, has both. That is the difference
between no way in and a way in that is not their heart language, and it is the
next action a mobiliser can take — which is why it belongs in the EXPORT and not
only in the map's sheet, where it has been sitting unexportable.

Four columns: the bridge language, its ISO code, the strength, and how many
groups in that country speak it. Keyed by code|country, because the same
language bridges differently in different places.

IT CLOSES NO GAP AND THE DEFINITION SAYS SO. A bridge is a way in, not a
translation. These 168 are still counted in "nothing found", because they still
have nothing in the language they think in, and the first believers among them
still cannot be trained from a text that does not exist.

The other 201 have nothing AND no bridge. That number is worth having on its own:
it is the isolated set, where neither their own language nor anything spoken
around them carries the gospel.

Verified end to end: the CSV exports 2,107 rows carrying Bridge language, Bridge
ISO, Bridge strength and Groups speaking it, and the Afitti row reads Arabic,
Sudanese.
2026-09-25 08:26:52 +00:00
doxa-maps-88655c70
c31df9f349 IBT is the best lead on this map, and recording why it is not a pool
36 Doxa groups carry an IMB "audio Scripture exists" flag while every pool here
says nothing. Asking why rather than shrugging: most are Dagestani and Caucasus
languages — Akhvakh, Andi, Bagvalal, Botlikh, Chamalal — which the
English-centred catalogues simply do not cover. The Institute for Bible
Translation in Moscow does, and their /languages page lists 54+ projects
including every one of those, with an ordinary robots.txt that allows it.

It still cannot be a pool. The page gives a native-script name, a region and a
language family and nothing else: no ISO code, no per-language page, no
publication URL. Joining it means matching "Akhvakh (ашвалӏи мицӏцІи)" to a
code by NAME, which is the guess this build removed two commits ago when Doxa's
own API made it unnecessary — the same guess that would have handed Akha in Laos
one of three sub-varieties.

What it is worth is the other thing the Driver asked for: the people who do the
translation work, so a group with nothing can be handed to a team that already
has the linguistic work and the relationships. For the Caucasus cluster, IBT is
that team, by name. That belongs in a leads table with a human confirming each
code, never auto-joined into the index.
2026-09-25 05:51:26 +00:00
doxa-maps-88655c70
c980d4e576 The Deaf gap is larger than this map was treating it as, and the route is a person
Cross-checking Doxa's roster against IMB's own access flags: 122 of their 2,107
people groups are DEAF — 17.5 million people — and 92 have nothing in this index.
For 33 of those 92 IMB records that material EXISTS, much of it dated 2021 at
"Selections" or "Stories": Armenian, Bulgarian, Myanmar, Cambodian and Cuban
sign languages among them. That is a coordinated body of work this build cannot
see.

Re-probed all three hosts so nobody spends another tick on them:

  doorinternational.org   the REAL DOOR International. Cloudflare 403 "Just a
                          moment..." on every path INCLUDING robots.txt. A bot
                          shield, and this repo does not circumvent shields — it
                          reached GRN through their own public site rather than
                          around it. Out on access, never on doctrine.
  deafbible.com           still the same 3,894-byte app shell for every URL,
                          /sitemap.xml included. robots allows everything; there
                          is simply nothing served to a fetch. api.deafbible.com
                          answers 525, so their own app's API is down too.
  door.international      NOT theirs. Parked for sale on GoDaddy, and it now
                          publishes an llms.txt saying exactly that. It looked
                          like a lead precisely because it advertises an LLM
                          policy. Their real domain is the first entry above.

This is the largest identified-but-unreachable set on the map. The route is
someone asking them for access, not another agent probing the same three hosts.
2026-09-25 05:46:52 +00:00
doxa-maps-88655c70
8423411757 The GRN sweep was defaulting to "none", not measuring it
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
Cross-checking IMB's own access flags against this index turned up 112 Doxa
groups where they say a resource exists and we found nothing. Nineteen of those
were gospel recordings — GRN, a pool this build had already swept — so one of the
two was wrong. Asking 5fish directly about six of them, with a real code and an
impossible one as controls: FOUR have recordings. alk Alak, nos Nisu Eastern,
jle Ngile, lma Limba East, each landing on its own numbered language page.

The fault is here. classify() read the page TITLE and its last rule was a
catch-all: any title matching neither known pattern became "none". That is a
default wearing the clothes of a finding, and it was silently losing every page
whose title was phrased in a way nobody had anticipated.

The landing decides now and the title only names. A published code lands on
/<id>?language=<name>; one they do not carry is swept to /regions — the same
discriminator already proved in band in the verifier and the rot sampler. A
request that errors leaves the code UNDECIDED rather than "none", because
recording a failed request as an absence is how this started.

GRN_RECHECK=1 re-asks the codes the old rule defaulted, and a hit can now take a
code back OUT of the none list — a recheck that could only add would be unable to
correct the thing it exists for. Proved on the six: 4 recovered, 2 confirmed
empty, controls passing.
2026-09-25 05:42:16 +00:00
doxa-maps-88655c70
26cd737587 All 2,107 of Doxa's groups, placed and searchable, from Doxa's own record
The map got coordinates and a language code by matching each group's NAME
against the pin dataset. That left 285 unplaceable and, worse, unsearchable: no
ISO code is no key to look them up by in any pool, so the access table could only
report them as "not searched" — nobody had been able to ask.

Doxa's own per-group endpoint carries both, and their robots.txt disallows
nothing. /api/people-groups/detail/<slug> gives primary_language and latitude and
longitude for every one: Akha to ahk, Akeu to byo, Alxa Mongols to xal, Ajja to
krs — four the name join had failed. So the name matching is not improved, it is
REMOVED. It was always a guess wearing a join's clothes: Akha in Laos sat beside
Akha Jipea, Akha Jaweuy and Akha Piso in the pins, and any pick among them would
have invented a language for a people. The publisher saying which language their
group speaks ends the guessing.

2,107 of 2,107 placed. Zero without a language code. The "not searched" rung is
gone from the access ladder because nothing is unsearched any more.

The gap grows 281 to 381, and that is the point: those 100 groups were always
without the gospel in their language, and the map could not say so because it did
not know what their language was.

The fetcher runs its controls in band both ways before trusting a record — a real
slug must return a language and coordinates, an impossible one must not — so a
site answering 200 with an empty body could not fill this table with silence.
IMB's own access and engagement fields come along beside ours, never merged into
them, because where two sources disagree that disagreement is worth seeing.
2026-09-25 05:35:11 +00:00
doxa-maps-88655c70
66750a59db The table the map is for: what each Doxa group can read, watch or hear
Some checks are pending
build-gate / build (push) Waiting to run
The Driver's question, in their words, is which of THEIR people groups have
gospel resources and which do not. Every tab here answered it for the research
corpus — 12,379 groups across 4,231 languages — which is the right scope for
finding what exists and the wrong one for working a list of 2,107. This one is
one row per group on Doxa's roster, it leads the tabs, and it exports.

The access ladder says what a person can DO, because that is what decides what
you send: read and watch 785 · watch or hear but cannot read 698 · read 53 ·
stories only 5 · nothing found 281. The second rung is the translation brief —
those groups can already receive the gospel and cannot yet study it, so the
first believers among them cannot be trained from a text that does not exist.

NO LANGUAGE CODE IS ITS OWN RUNG. The first run collapsed it into "nothing
found" and reported 566, when 281 had been searched and come back empty and 285
had never been searchable at all — no ISO 639-3, so no pool has a key to look
them up by. That would have told a mobiliser 285 peoples have no gospel in their
language when the truth is nobody has been able to ask. It is an identification
problem before it is a translation one.

CSV carries the filter and the roster: the whole table exports 2,107 rows, China
exports 154, and German exports a header and nothing else because German is not
one of Doxa's groups.
2026-09-25 05:07:53 +00:00
doxa-maps-88655c70
5db368f94c Place 98 more of Doxa's groups by reading the misses instead of guessing at them
Some checks are pending
build-gate / build (push) Waiting to run
383 of the roster had no coordinates — 18% of Doxa's people invisible on the map
and missing from every count on it. Reading why, rather than reaching for fuzzy
matching, one class turns out to be a spelling convention: the roster puts the
qualifier first and the pin dataset puts it last, around a comma.

  Acre Arara <-> Arara, Acre        Ashkuni Nuristani <-> Nuristani Ashkuni
  Banka Samogho <-> Samogho, Banka  Butuo Nosu <-> Nosu, Butuo

Same words, same country, different order. Matching on the sorted token set
absorbs exactly that: 94 recovered, plus 4 that only needed diacritics folded,
and every one resolved to a SINGLE pin inside its own country. Spot-checked
against the languages they landed on — Ashkuni Nuristani to Ashkun [ask] in
Afghanistan, Banka Samogho to Bankagooma [bxw] in Mali — and all 94 carry a
language code, so the resource join holds.

What it refuses is the point. Doxa lists Akha in Laos; the pins carry Akha
Jipea, Akha Jaweuy and Akha Piso — a parent against three sub-varieties, and
picking one would invent a language for a people. Doxa lists Akeu in China; the
nearest pin is Ake, a different language. Two candidates is a refusal, not a
coin toss. 285 still have no candidate at all and stay unplaced, because they
need a source this build does not have rather than a looser string match.

1,822 placed now. The gap grows 268 to 281, which is the join telling the truth.
2026-09-25 04:58:37 +00:00
doxa-maps-88655c70
ab80c5c21a The roster this map should have been drawing: Doxa's 2,107 people groups
The map was built on the full 12,379-group dataset. Doxa works the groups on
pray.doxa.life, so a map of everyone buries the ones they can reach and paints
countries by a gap that is not theirs.

The roster is now its own table, from their own API: 2,107 groups across 175
countries, 206 million people. 1,724 join a pin by (name, country) and every one
of those carries an ISO 639-3 code, so the resource join is intact. The other 383
are kept with mapped:false rather than dropped — they are Doxa's groups whether
or not this build can place them yet.

The measurement that settles the colour complaint: 106 of those 175 countries
hold one or two Doxa groups, and the United States holds three. The choropleth
paints a country by the SHARE of its groups with nothing found, so one group of
three turns the whole United States amber. That is the mainly-yellow map, and
underneath it is not a palette problem — it is a percentage over a denominator
of three being drawn as though it meant something. A share is only meaningful in
the 35 countries holding eight or more.

Nothing is lost: every index, verified link and sweep result stays where it is.
2026-09-25 04:34:55 +00:00
doxa-maps-88655c70
4fc261f8df Say which film it is: three languages were labelled JESUS Film without one
Five codes still carried the retired ?languageId= URL, which nothing can verify.
Asking Arclight what each language actually has:

  ars  Arabic, Najdi        The Savior                 (8 groups; film was their ONLY resource)
  gjk  Kachi Koli           Magdalena - Director's Cut
  mzn  Mazanderani          Magdalena - Director's Cut
  lia  Limba, West-Central  no feature at all
  jos  Jordanian Sign Lang  left alone, see below

counts.featureFilm >= 1 says a feature exists, not that it is JESUS. Three of
these had a real JESUS Film Project film in their own language under a different
title, and the map called all of them the JESUS Film — the same class of lie as
calling Open Bible Stories Scripture. They now ship under their own names, with
verified deep links; lia's film is dropped, because no component carries it and
a link nobody can check is worse than none.

jos is left exactly as it was. Arclight files iso3 jos under a row whose own
name reads Lebanese Sign Language, while the JESUS Film sits at a different id
named Jordanian Sign Language. The ISO join and the publisher's label disagree,
and resolving that is a data question, not a guess to make inside a fetcher.
2026-09-25 03:44:39 +00:00
doxa-maps-88655c70
c3d1df30dc The click-sheet verifier had not run in months, and was aimed at the wrong files
It crashed on ENOENT — app/doxa-maps/gospel-resources/assets/tables/, a tree
deleted when data moved out of public/. Nothing noticed, because nothing ran it:
it had no npm script, so it was neither discoverable nor wired anywhere.

The dead path was the smaller half. Its other input was a private by-country
copy under plugin-designs/, and its people-group table was the curated 2,106-row
subset that resources-map.vue stopped fetching some time ago — it says so in its
own comments; the country sheet is built from the 12,379-pin index now. Repaired
in place it would have gone green about a surface it was no longer looking at.

Both inputs are the live tables. The country join is ISO3 instead of a substring
test against a comma-joined name blob, which the file's own comment admitted was
under-counting because country names contain commas. It reads 242 of 242
countries resolving to a pin and 8,435 sheet links, through the same flattener
the sheet renders from, and its self-test now requires four corruptions to go
red rather than two.

Also gives it and the stale-sweep gate an npm script, and corrects the link-rot
entry, which still told readers that jesusfilm.org and fivefish.org can never be
status-checked — the thing that gate stopped believing two commits ago.
2026-09-25 03:33:31 +00:00
doxa-maps-88655c70
a71f676fc3 Give the country sheet back the number its own footnote depends on
The two curated figures — growing & unreached, deaths a day — sit under a
footnote saying how much narrower their basis is than the pins. It read "covers
391 of these 391 groups — a narrower set than the pins" for every one of the 242
countries, because the script that recomputes country rows from the pins
overwrote the curated group count with the pin count and the footnote went on
reading it. Always false, and false about the one thing it existed to qualify.

The real numbers were still in the commit that introduced the overwrite and are
restored on the rows: China 158 of 391, India 856 of 2,146. They are recorded
once now and never rewritten, because the pins change every build and a curated
analysis does not.

The same block also drew both figures for the 72 countries that have no curated
row at all — "Est. deaths/day 0", which is not a measurement. It is hidden for
them instead. A first pass at the fix handed those 72 a basis equal to their pin
count, inventing the number the footnote exists to qualify; null now means there
is no curated set here.
2026-09-25 03:19:00 +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
2046f2febd Export which kind of gap each row is, and make both surfaces agree
The map explains that "1,845 groups have nothing" is four situations
wearing one number. An exported copy of the gap table could not say
that at all, which is the copy a translation organisation actually
works from. Each row now carries its kind:

  1,001  every reachable pool checked — the real translation gap
    660  GRN not yet swept — the crawl is still running
    183  sign language — pools are key-gated, an access problem
      1  no language code — no pool can index it

Adding it exposed that the two surfaces bucketed differently. The
table tests sign language first; the map tested private-use first, so
47 Deaf groups were filed under "unindexable code" and the Deaf share
read 136 instead of 183. Both were internally honest and they told
different stories, which is worse than either being wrong on its own.

The map now uses the table's order, because being Deaf is the more
actionable fact about those rows than the shape of their code, and it
still says 47 of them are doubly stuck. Both surfaces report 1,001 /
660 / 183 / 1, verified against each other rather than separately.

The meta warns that filtering to sign_language alone is now the whole
Deaf figure and not a subset, since the private-use rows are the same
people.
2026-09-25 00:22:11 +00:00