From 64e9fb33ef69ebe28594d83626bd404a83f36e1d Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Sat, 26 Sep 2026 07:56:31 +0000 Subject: [PATCH] Guard the last three catalogue snapshots against a bad network day MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- .../gospel-resources/scripts/fetch-country-languages.mjs | 8 ++++++-- .../gospel-resources/scripts/fetch-ebible-credits.mjs | 8 ++++++-- .../gospel-resources/scripts/fetch-sun-deaf-index.mjs | 8 ++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/plugin-designs/gospel-resources/scripts/fetch-country-languages.mjs b/plugin-designs/gospel-resources/scripts/fetch-country-languages.mjs index 48f9c8957..defedfc06 100644 --- a/plugin-designs/gospel-resources/scripts/fetch-country-languages.mjs +++ b/plugin-designs/gospel-resources/scripts/fetch-country-languages.mjs @@ -26,6 +26,7 @@ * country on no authority at all. */ import { writeFileSync } from 'node:fs' +import { writeSnapshot } from './_snapshot-guard.mjs' import { resolve, dirname } from 'node:path' import { fileURLToPath } from 'node:url' @@ -104,7 +105,9 @@ if (countries < 150) { console.error(' A thin file would silently narrow every bridge recommendation that reads it.') process.exitCode = 1 } else { - writeFileSync(OUT, `${JSON.stringify({ + /* THROUGH THE SNAPSHOT GUARD. The 150-country floor above catches a collapse; it does not + catch the run that parses 240 countries and loses a fifth of them. */ + const body = `${JSON.stringify({ meta: { title: 'Languages spoken per country, with each share of the country', sources: [CLDR, ISO], @@ -117,7 +120,8 @@ if (countries < 150) { territories_dropped_no_alpha3: noAlpha3, }, byCountry, - }, null, 1)}\n`) + }, null, 1)}\n` + writeSnapshot(OUT, body, (j) => Object.keys(j.byCountry || {}).length, 'country(ies)') console.log(`country-languages: ${countries} countries (keyed by ISO3), ${dropped} CLDR tag(s) dropped for having no ISO 639-3, ${noAlpha3} territory(ies) with no alpha-3`) console.log(`wrote ${OUT}`) } diff --git a/plugin-designs/gospel-resources/scripts/fetch-ebible-credits.mjs b/plugin-designs/gospel-resources/scripts/fetch-ebible-credits.mjs index e9d318740..e55307409 100644 --- a/plugin-designs/gospel-resources/scripts/fetch-ebible-credits.mjs +++ b/plugin-designs/gospel-resources/scripts/fetch-ebible-credits.mjs @@ -21,6 +21,7 @@ * It adds no resource and moves no tier. It is attribution for Scripture already in the index. */ import { writeFileSync, readFileSync } from 'node:fs' +import { writeSnapshot } from './_snapshot-guard.mjs' import { resolve, dirname } from 'node:path' import { fileURLToPath } from 'node:url' @@ -113,7 +114,9 @@ if (!joined) { console.error('CREDITS: REFUSING TO WRITE — nothing joined; the URL shape or the catalogue changed.') process.exitCode = 1 } else { - writeFileSync(OUT, `${JSON.stringify({ + /* THROUGH THE SNAPSHOT GUARD. The refusal above fires only when NOTHING joins. A catalogue + fetch that half-succeeds joins some, and would take the translator's name off the rest. */ + const body = `${JSON.stringify({ meta: { title: 'Who translated it — attribution for the Scripture this map links to', source: SRC, @@ -128,7 +131,8 @@ if (!joined) { 'doctrine screen has never seen. Adds no resource and moves no tier.', }, byCode: used, - }, null, 1)}\n`) + }, null, 1)}\n` + writeSnapshot(OUT, body, (j) => Object.keys(j.byCode || {}).length, 'credited code(s)') console.log(`translation-credits: ${joined} code(s) with a named translator, ${licenceOnly} whose copyright is a licence, ${noRow} with no catalogue row, from ${raw.length - 1} rows`) console.log(`wrote ${OUT}`) } diff --git a/plugin-designs/gospel-resources/scripts/fetch-sun-deaf-index.mjs b/plugin-designs/gospel-resources/scripts/fetch-sun-deaf-index.mjs index 295822f5d..5f544985c 100644 --- a/plugin-designs/gospel-resources/scripts/fetch-sun-deaf-index.mjs +++ b/plugin-designs/gospel-resources/scripts/fetch-sun-deaf-index.mjs @@ -40,6 +40,7 @@ * asl.bible, 4.bible do not resolve. */ import { writeFileSync } from 'node:fs' +import { writeSnapshot } from './_snapshot-guard.mjs' import { resolve, dirname } from 'node:path' import { fileURLToPath } from 'node:url' @@ -116,7 +117,9 @@ if (verified.length < 3 || !projects.length) { console.error(' Shipping a thin index would quietly shrink the only bridge the Deaf gap has.') process.exitCode = 1 } else { - writeFileSync(OUT, `${JSON.stringify({ + /* THROUGH THE SNAPSHOT GUARD. The floor above is 3 verified pages, which a rate-limited run + can clear while losing most of the projects — and this file is the Deaf gap's only bridge. */ + const body = `${JSON.stringify({ meta: { title: 'SUN — a universal signed language with Scripture, for Deaf groups with nothing', source: 'sun.bible, fetched and verified by content', @@ -138,7 +141,8 @@ if (verified.length < 3 || !projects.length) { }, resources: verified.map(({ key, url, label, title, bytes }) => ({ key, url, label, title, bytes })), projects, - }, null, 1)}\n`) + }, null, 1)}\n` + writeSnapshot(OUT, body, (j) => (j.resources || []).length + (j.projects || []).length, 'SUN page(s) and project country(ies)') console.log(`SUN: ${verified.length} verified page(s), ${projects.length} project country(ies)`) console.log(`wrote ${OUT}`) }