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.
86 lines
4.1 KiB
JavaScript
86 lines
4.1 KiB
JavaScript
/**
|
|
* 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.`)
|