doxa-maps/plugin-designs/gospel-resources
doxa-maps-88655c70 0e0b142719 A half-finished catalogue fetch can no longer overwrite a good one
The FCBH fetcher read per-fileset video scope for 1,666 languages and that file was
committed. A later run, while live.bible.is was rate-limiting, produced 1,103 and
overwrote the good file with the degraded one. Nothing failed, nothing warned, and the
artefact silently lost a third of its content. It was caught only because git status
happened to be read, and the fix was a git checkout.

That is the third time this shape has appeared here:
  - a VERIFY_LIMIT smoke run replaced the full verification table with 30 rows;
  - dropping a dead link removed it from the corpus, so the next run forgot it was
    dead and the build put it straight back;
  - this one.
Every snapshot in this bundle is built from a network read, and a network read has a bad
day. The dangerous case is never the run that throws - that leaves the old file alone -
it is the run that half-succeeds and writes anyway.

scripts/_snapshot-guard.mjs now stands in front of every catalogue write. A snapshot may
grow freely and may shrink a little, but losing more than a tenth of the previous run is
treated as a failed fetch: the existing file is left untouched and the script exits 1.
It is deliberately not an equality check, because catalogues do genuinely lose entries
when a publisher retires something, and a gate nobody can satisfy gets switched off.
SNAPSHOT_FORCE=1 is the one loud way past it.

Proven by replaying the exact failure: feeding it the 1,103-entry version refuses the
write, names the real numbers (563 fewer, 13.8%, below the 90% floor) and leaves the
file byte-identical to the good one. SNAPSHOT_FORCE=1 writes it. An equal write passes.

All six fetchers are wired: ebible, arclight, arclight-share-urls, door43, zume, fcbh.
Re-running zume, door43 and ebible for real passes the guard at 52, 208 and 1,237 codes
and produces byte-identical data, so the guard perturbs nothing.

The one exception is the checkpoint write in the resumable share-urls fetcher, which
starts from the existing file as a cache and can only grow. It must now declare itself
with a CHECKPOINT-WRITE-OK marker, so the exemption is a statement rather than an
oversight.

New gate check:snapshot-guard fails if any fetch-*.mjs writes OUT directly without that
marker, or never calls writeSnapshot at all - a guard that is easy to bypass is a guard
that will be bypassed. It refuses to run against an empty file list rather than passing
vacuously, and --poison injects a raw write and requires the check to catch it. Chained
into build:unlocked and its doc copy kept in sync.
2026-09-24 11:59:48 +00:00
..
data Stop claiming Scripture in a language recorded as "Undetermined" 2026-09-24 05:22:59 +00:00
scripts A half-finished catalogue fetch can no longer overwrite a good one 2026-09-24 11:59:48 +00:00
charts.vue Audio Scripture in 2,121 languages: every pin used to say "not searched" 2026-09-24 11:16:30 +00:00
data-tables.vue Describe the plugin that exists now, in one line that survives extraction 2026-09-24 08:26:50 +00:00
gospel-dashboard.meta.json The gospel resources dashboard: the map above its own tables, in one element 2026-09-24 08:42:17 +00:00
gospel-dashboard.vue The gospel resources dashboard: the map above its own tables, in one element 2026-09-24 08:42:17 +00:00
index.html Session work: strobe fix, grid layout, stacked tree legend, selection camera, chip bounding, asset inheritance, gates 2026-09-20 04:49:25 +00:00
index.js Complete app-profiles->plugin-designs sweep across comments/docs; build green; layouts kept at root (load-bearing) 2026-09-17 18:18:19 +00:00
resources-list.vue Bible video from FCBH, with the real book scope on every link 2026-09-24 11:47:37 +00:00
resources-map.vue Bible video from FCBH, with the real book scope on every link 2026-09-24 11:47:37 +00:00
SOURCES-CHECKED-AND-REJECTED.md Bible video from FCBH, with the real book scope on every link 2026-09-24 11:47:37 +00:00