Correct the record: the dead bundle was my merge, not a duplicate import

dx-shepherd's verifier traced it. Merge 52ab6089 from this worktree committed
public/doxa-maps/gospel-resources/gospel-resources.js with 21 conflict markers —
both sides concatenated, 1.13 MB — and that is what threw "Identifier Ge has
already been declared" and killed every gospel leaf.

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

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

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

Also verifies the 8 links the GRN recheck recovered, which check:gospel-all
caught as shipped-but-unverified on its very first run — psi, alk, nos, jle, lma
and three more, all now confirmed landing on their own 5fish language pages.
This commit is contained in:
doxa-maps-88655c70 2026-09-25 10:18:16 +00:00
parent beb13da3da
commit 59e9695aaa
7 changed files with 261 additions and 22 deletions

View file

@ -4991,7 +4991,7 @@
"religion": "Islam - Sunni",
"population": 5500,
"people_praying": 0,
"people_committed": 0,
"people_committed": 1,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",
@ -16751,7 +16751,7 @@
"religion": "Islam - Sunni",
"population": 57500,
"people_praying": 2,
"people_committed": 0,
"people_committed": 1,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",
@ -34055,7 +34055,7 @@
"religion": "Hinduism",
"population": 700,
"people_praying": 1,
"people_committed": 2,
"people_committed": 3,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",
@ -34319,7 +34319,7 @@
"religion": "Islam - Folk",
"population": 12000,
"people_praying": 1,
"people_committed": 4,
"people_committed": 3,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",
@ -43463,7 +43463,7 @@
"religion": "Islam - Sunni",
"population": 23000,
"people_praying": 0,
"people_committed": 1,
"people_committed": 2,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",

View file

@ -1,7 +1,7 @@
{
"meta": {
"title": "Gospel resource link verification",
"generated": "2026-09-25T03:43:08.467Z",
"generated": "2026-09-25T09:56:59.860Z",
"method": "HEAD, redirects followed. verified = HTTP 200. broken = 404/410. unknown = unreachable, an ambiguous status, or a URL shape that cannot be checked by fetch.",
"scope": "Every URL in data/tables/people-group-pins.json, deduplicated.",
"states": {
@ -96,12 +96,12 @@
]
},
"summary": {
"records": 8516,
"checkedThisPass": 3,
"records": 8524,
"checkedThisPass": 8,
"carriedForward": 74,
"pruned": 0,
"broken": 74,
"verified": 8437,
"verified": 8445,
"unknown": 5
},
"byHost": {
@ -142,7 +142,7 @@
"verified": 921
},
"fivefish.org": {
"verified": 1022
"verified": 1030
}
}
},
@ -42726,6 +42726,46 @@
"state": "verified",
"detail": "HTTP 200 (via https://www.jesusfilm.org/watch/the-savior.html/arabic-najdi.html)",
"at": "2026-09-25T03:43:07.944Z"
},
"https://fivefish.org/lma": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/12954?language=Limba%2C%20East (the site answers 200 for any path)",
"at": "2026-09-25T09:55:49.572Z"
},
"https://fivefish.org/nev": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/23092?language=Nyaheun (the site answers 200 for any path)",
"at": "2026-09-25T09:55:59.632Z"
},
"https://fivefish.org/ngt": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/23045?language=Kriang (the site answers 200 for any path)",
"at": "2026-09-25T09:56:09.574Z"
},
"https://fivefish.org/alk": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/23050?language=Alak (the site answers 200 for any path)",
"at": "2026-09-25T09:56:19.687Z"
},
"https://fivefish.org/nos": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/4976?language=Nisu%2C%20Eastern (the site answers 200 for any path)",
"at": "2026-09-25T09:56:29.456Z"
},
"https://fivefish.org/lbo": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/12797?language=Laven (the site answers 200 for any path)",
"at": "2026-09-25T09:56:39.531Z"
},
"https://fivefish.org/jle": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/14912?language=Ngile (the site answers 200 for any path)",
"at": "2026-09-25T09:56:49.486Z"
},
"https://fivefish.org/psi": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/15537?language=Pashai%2C%20Southeast (the site answers 200 for any path)",
"at": "2026-09-25T09:56:59.503Z"
}
}
}

View file

@ -0,0 +1,86 @@
/**
* check-gospel-all.mjs — everything this bundle owns, in one command.
*
* WHY THIS EXISTS. gospel-resources has ten gates of its own and every one of them was
* hand-run. That is how a dead bundle shipped: a duplicate module killed all five leaves,
* and the one gate I happened to run that day was pointed at the map. `check:no-orphan-gates`
* was right that they were all reachable — reachable is not the same as run.
*
* It is NOT wired into gates:pre/post on purpose. Three of these need a browser, the shared
* wave holds browser gates under load, and a bundle owner should not be able to slow every
* other agent's build by adding to a list they do not own. This is the bundle's own front
* door instead: one command, honest about what it could not measure.
*
* STATIC FIRST, THEN BROWSER. The static gates are sub-second and catch most drift, so they
* run first and a failure there stops the run before a browser is launched — there is no
* point spending a scarce browser slot to confirm a table is inconsistent.
*
* NOT MEASURED IS NOT PASS. A browser gate that cannot get a slot exits 75 and is reported
* as NOT MEASURED, never folded into the pass. Anything else would turn a busy machine into
* a green tick.
*/
import { spawnSync } from 'node:child_process'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const HERE = dirname(fileURLToPath(import.meta.url))
const REPO = resolve(HERE, '..')
const SLOT = resolve(HERE, 'browser-slot.sh')
const STATIC = [
['check:no-conflict-markers', 'nothing tracked is mid-merge'],
['check:no-conflict-markers:poison', '...and that check can fail'],
['check:coverage-consistent', 'the surfaces agree about who has what'],
['check:coverage-consistent:poison', '...and that check can fail'],
['check:surfaces-read-the-tier', 'one place decides "this group has nothing"'],
['check:stale-sweep-claim', 'no caveat outliving its truth'],
['check:click-sheet', 'the country sheet resolves, links are absolute'],
['check:snapshot-guard', 'no fetcher writes around the guard'],
]
const BROWSER = [
['check:leaves-mount', 'all five leaves actually mount'],
['check:leaves-mount:poison', '...and that check can fail'],
['check:composed-filters', 'the composite filters instead of navigating'],
]
const run = (script, viaSlot) => {
const cmd = viaSlot ? SLOT : 'bun'
const args = viaSlot ? ['bun', 'run', script] : ['run', script]
const r = spawnSync(cmd, args, { cwd: REPO, encoding: 'utf8' })
return { code: r.status ?? 1, out: `${r.stdout || ''}${r.stderr || ''}` }
}
const failed = []
const notMeasured = []
let passed = 0
for (const [script, what] of STATIC) {
const r = run(script, false)
if (r.code === 0) { passed++; console.log(` ok ${script.padEnd(34)} ${what}`) }
else { failed.push([script, r.out.trim().split('\n').slice(-3).join(' | ')]); console.log(` FAIL ${script.padEnd(34)} ${what}`) }
}
if (failed.length) {
console.error(`\nGOSPEL-ALL: FAIL — ${failed.length} static gate(s); not launching a browser to confirm it.`)
for (const [s, tail] of failed) console.error(` ${s}: ${tail}`)
process.exit(1)
}
for (const [script, what] of BROWSER) {
const r = run(script, true)
if (r.code === 0) { passed++; console.log(` ok ${script.padEnd(34)} ${what}`) }
else if (r.code === 75) { notMeasured.push(script); console.log(` -- ${script.padEnd(34)} NOT MEASURED (no browser slot)`) }
else { failed.push([script, r.out.trim().split('\n').slice(-3).join(' | ')]); console.log(` FAIL ${script.padEnd(34)} ${what}`) }
}
console.log('')
if (failed.length) {
console.error(`GOSPEL-ALL: FAIL — ${failed.length} gate(s) red, ${passed} green, ${notMeasured.length} not measured.`)
for (const [s, tail] of failed) console.error(` ${s}: ${tail}`)
process.exit(1)
}
if (notMeasured.length) {
console.log(`GOSPEL-ALL: INCOMPLETE — ${passed} green, ${notMeasured.length} NOT MEASURED (${notMeasured.join(', ')}).`)
console.log(' Not a pass. Re-run when a browser slot is free.')
process.exit(75)
}
console.log(`GOSPEL-ALL: PASS — ${passed} gate(s), static and browser.`)

View file

@ -0,0 +1,68 @@
/**
* check-no-conflict-markers.mjs — nothing tracked may carry an unresolved merge.
*
* THE INCIDENT. A merge commit from this worktree landed the BUILT bundle
* public/doxa-maps/gospel-resources/gospel-resources.js with 21 conflict markers in it —
* both sides concatenated, 1.13 MB. The browser threw "Identifier Ge has already been
* declared" and every gospel leaf rendered dead, while the SRI hash still matched, every
* request still returned 200, and no probe could see an error.
*
* THE CAUSE WAS A LANDING SCRIPT, NOT A BAD MERGE. The loop resolved the one file it
* expected to conflict and then ran `git add -A public`, which stages EVERY file under
* public/ — including the ones still holding markers. A one-line convenience that turns a
* conflict git correctly refused to guess at into a committed artefact.
*
* AND THE FIRST DIAGNOSIS WAS WRONG. I attributed the duplicate identifier to a module
* imported twice and re-routed an import to "fix" it. That change was harmless but it was
* not the cause; the rebuild that came with it is what repaired the file. A plausible cause
* that a fix appears to confirm is the easiest kind of wrong answer, and the only defence is
* a check that names the real condition.
*
* THE RULE, for a BUILT file especially: never hand-merge, never take both. Take either
* side, REBUILD, commit the rebuild. Then run this before pushing.
*/
import { execSync } from 'node:child_process'
import { readFileSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const HERE = dirname(fileURLToPath(import.meta.url))
const REPO = resolve(HERE, '..')
const POISON = process.argv.includes('--poison')
/* Built from char codes so this file never contains a marker and never trips itself. */
const OPEN = String.fromCharCode(60).repeat(7)
const MID = String.fromCharCode(61).repeat(7)
const CLOSE = String.fromCharCode(62).repeat(7)
const MARKER = new RegExp('^(' + OPEN + '|' + MID + '$|' + CLOSE + ')', 'm')
const scan = (text) => MARKER.test(text)
if (POISON) {
const fake = 'a\n' + OPEN + ' HEAD\nmine\n' + MID + '\ntheirs\n' + CLOSE + ' other\n'
if (!scan(fake)) {
console.error('NO-CONFLICT-MARKERS: SELF-TEST FAIL — a textbook conflict was not detected.')
process.exit(1)
}
console.log('NO-CONFLICT-MARKERS: SELF-TEST PASS — a textbook conflict is detected.')
process.exit(0)
}
const tracked = execSync('git ls-files -z', { cwd: REPO, maxBuffer: 1 << 28 })
.toString('utf8').split(String.fromCharCode(0)).filter(Boolean)
const hits = []
for (const f of tracked) {
let text
try { text = readFileSync(resolve(REPO, f), 'utf8') } catch { continue }
if (text.indexOf(String.fromCharCode(0)) >= 0) continue
if (scan(text)) hits.push(f)
}
if (hits.length) {
console.error('NO-CONFLICT-MARKERS: FAIL — ' + hits.length + ' tracked file(s) carry an unresolved merge:')
for (const f of hits.slice(0, 10)) console.error(' ' + f)
console.error(' For a BUILT file: take either side, REBUILD the bundle, commit the rebuild.')
console.error(' Never "add -A" a directory mid-merge — it stages the markers with everything else.')
process.exit(1)
}
console.log('NO-CONFLICT-MARKERS: PASS — ' + tracked.length + ' tracked file(s), none mid-merge.')

View file

@ -197,7 +197,10 @@
"app:start-page": "node internal/set-app-start-page.mjs",
"check:app-start-page": "node internal/set-app-start-page.mjs --check",
"check:leaves-mount": "node internal/check-gospel-leaves-mount.mjs",
"check:leaves-mount:poison": "node internal/check-gospel-leaves-mount.mjs --poison"
"check:leaves-mount:poison": "node internal/check-gospel-leaves-mount.mjs --poison",
"check:gospel-all": "node internal/check-gospel-all.mjs",
"check:no-conflict-markers": "node internal/check-no-conflict-markers.mjs",
"check:no-conflict-markers:poison": "node internal/check-no-conflict-markers.mjs --poison"
},
"//scripts": {
"_": "What each script does. These notes used to sit inside the command strings as trailing \"# …\" comments, which made the shell discard any arguments passed after \"--\" (so a gate given --url silently measured its hardcoded default port — possibly another agent's server). Keep notes here, never in a command.",
@ -297,7 +300,9 @@
"check:vision-card-numbers": "the DOXA Vision Card must show the numbers the DATA gives: independently recomputes all four funnel steps (and DOXA-in-frontier) from people_groups.csv + the saved DOXA list, compares with vision-funnel.json, and with --url also with the numbers painted in the card. A typed-in number, a stale funnel file or a rendering bug fails it.",
"check:map-embed-cells": "PRODUCTION-MATRIX cells B (iframe) and C (script embed) for the seven Research Gallery maps on a genuinely foreign origin (tree on 127.0.0.1, partner page on localhost). C passes the HOST a distinct token and fails if any Mapbox request uses another. --only bundle/profile,… --cell B|C.",
"check:gallery-embed:live-cors": "the Research Gallery on a foreign page under the LIVE static layer's CORS rule (Access-Control-Allow-Origin on files inside the tree's folders, NOT on the root manifest.json, measured on nuxtbase): it must still boot every tab, by reading the CORS-readable manifest copy generate-tree writes beside its bundle.",
"check:leaves-mount": "DID THE ELEMENT ACTUALLY MOUNT, on all five gospel leaves. Exists because a ReferenceError thrown during setup dies INSIDE the custom-element boundary: the page loads, every asset returns 200, pageerror fires zero times, and the element sits there with an empty shadow root. A healthy-looking dead page. This asks the cheap unambiguous question the rich gates are too slow to ask on every leaf — is anything there — in five steps that each name their own failure: tag defined, host present, shadowRoot attached, shadow root non-empty, visible text rendered. It does NOT trust the absence of console errors; zero errors is not the same as working. --poison for the negative test."
"check:leaves-mount": "DID THE ELEMENT ACTUALLY MOUNT, on all five gospel leaves. Exists because a ReferenceError thrown during setup dies INSIDE the custom-element boundary: the page loads, every asset returns 200, pageerror fires zero times, and the element sits there with an empty shadow root. A healthy-looking dead page. This asks the cheap unambiguous question the rich gates are too slow to ask on every leaf — is anything there — in five steps that each name their own failure: tag defined, host present, shadowRoot attached, shadow root non-empty, visible text rendered. It does NOT trust the absence of console errors; zero errors is not the same as working. --poison for the negative test.",
"check:gospel-all": "EVERYTHING gospel-resources owns, in one command. Its ten gates were all hand-run, which is how a dead bundle shipped: a duplicate module killed all five leaves and the one gate run that day was pointed at the map. check:no-orphan-gates was right that they were reachable — reachable is not run. Deliberately NOT in gates:pre/post: three need a browser, the shared wave holds browser gates under load, and a bundle owner should not be able to slow everyone else's build by adding to a list they do not own. Static gates run first and stop the run on failure, so a scarce browser slot is never spent confirming a table is inconsistent. Exit 75 = NOT MEASURED, never folded into a pass.",
"check:no-conflict-markers": "Nothing tracked may carry an unresolved merge. Exists because a merge from this worktree committed a BUILT bundle with 21 conflict markers - both sides concatenated, 1.13MB - and every gospel leaf rendered dead while the SRI hash matched and every request returned 200. The cause was a landing script that resolved one expected file then ran \"git add -A public\", staging every other file including the ones still holding markers. For a built file: take either side, REBUILD, commit the rebuild, then run this before pushing. --poison for the negative test."
},
"devDependencies": {
"@capacitor/android": "^7.6.9",

View file

@ -4991,7 +4991,7 @@
"religion": "Islam - Sunni",
"population": 5500,
"people_praying": 0,
"people_committed": 0,
"people_committed": 1,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",
@ -16751,7 +16751,7 @@
"religion": "Islam - Sunni",
"population": 57500,
"people_praying": 2,
"people_committed": 0,
"people_committed": 1,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",
@ -34055,7 +34055,7 @@
"religion": "Hinduism",
"population": 700,
"people_praying": 1,
"people_committed": 2,
"people_committed": 3,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",
@ -34319,7 +34319,7 @@
"religion": "Islam - Folk",
"population": 12000,
"people_praying": 1,
"people_committed": 4,
"people_committed": 3,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",
@ -43463,7 +43463,7 @@
"religion": "Islam - Sunni",
"population": 23000,
"people_praying": 0,
"people_committed": 1,
"people_committed": 2,
"adopted_by_churches": 0,
"mapped": true,
"matched_by": "doxa detail",

View file

@ -1,7 +1,7 @@
{
"meta": {
"title": "Gospel resource link verification",
"generated": "2026-09-25T03:43:08.467Z",
"generated": "2026-09-25T09:56:59.860Z",
"method": "HEAD, redirects followed. verified = HTTP 200. broken = 404/410. unknown = unreachable, an ambiguous status, or a URL shape that cannot be checked by fetch.",
"scope": "Every URL in data/tables/people-group-pins.json, deduplicated.",
"states": {
@ -96,12 +96,12 @@
]
},
"summary": {
"records": 8516,
"checkedThisPass": 3,
"records": 8524,
"checkedThisPass": 8,
"carriedForward": 74,
"pruned": 0,
"broken": 74,
"verified": 8437,
"verified": 8445,
"unknown": 5
},
"byHost": {
@ -142,7 +142,7 @@
"verified": 921
},
"fivefish.org": {
"verified": 1022
"verified": 1030
}
}
},
@ -42726,6 +42726,46 @@
"state": "verified",
"detail": "HTTP 200 (via https://www.jesusfilm.org/watch/the-savior.html/arabic-najdi.html)",
"at": "2026-09-25T03:43:07.944Z"
},
"https://fivefish.org/lma": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/12954?language=Limba%2C%20East (the site answers 200 for any path)",
"at": "2026-09-25T09:55:49.572Z"
},
"https://fivefish.org/nev": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/23092?language=Nyaheun (the site answers 200 for any path)",
"at": "2026-09-25T09:55:59.632Z"
},
"https://fivefish.org/ngt": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/23045?language=Kriang (the site answers 200 for any path)",
"at": "2026-09-25T09:56:09.574Z"
},
"https://fivefish.org/alk": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/23050?language=Alak (the site answers 200 for any path)",
"at": "2026-09-25T09:56:19.687Z"
},
"https://fivefish.org/nos": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/4976?language=Nisu%2C%20Eastern (the site answers 200 for any path)",
"at": "2026-09-25T09:56:29.456Z"
},
"https://fivefish.org/lbo": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/12797?language=Laven (the site answers 200 for any path)",
"at": "2026-09-25T09:56:39.531Z"
},
"https://fivefish.org/jle": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/14912?language=Ngile (the site answers 200 for any path)",
"at": "2026-09-25T09:56:49.486Z"
},
"https://fivefish.org/psi": {
"state": "verified",
"detail": "5fish redirected to its language page https://fivefish.org/15537?language=Pashai%2C%20Southeast (the site answers 200 for any path)",
"at": "2026-09-25T09:56:59.503Z"
}
}
}