Merge remote-tracking branch 'origin/main' into shepherd/merge-b3
Some checks are pending
build-gate / build (push) Waiting to run

This commit is contained in:
doxa-maps-88655c70 2026-09-24 14:24:48 +00:00
commit 4eec165a2b
2 changed files with 31 additions and 4 deletions

View file

@ -53,6 +53,14 @@ await page.goto(pathToFileURL(file).href, { waitUntil: 'networkidle' })
// Resolve the rhythm words for THIS packet's language from the shared catalog.
// Per-key fallback to English, the same contract the renderer honours.
const lang = await page.evaluate(() => document.documentElement.lang || 'en')
// ★ WHAT THE PACKET SAYS ABOUT ITS OWN BUILD. packet.mjs stamps data-packet-build with the
// flags it was generated under ("full", "nophotos", "nomap", or a combination). Without it
// the only way to tell a fast layout-iteration build from a shippable one was for the PERSON
// RUNNING THE GATE to remember and pass PACKET_EXPECT_PHOTOS=0 — which is why the sweep sat
// RED in doxa-maps-plugin-studio on two probe artifacts nobody intended to ship.
// The ARTEFACT declares its own intent; the env vars below remain as an override for packets
// generated before this stamp existed.
const build = await page.evaluate(() => document.documentElement.getAttribute('data-packet-build') || '')
// RTL languages must actually be laid out RTL. Without dir="rtl" an Arabic packet
// renders readable glyphs LEFT-aligned — it looks like a styling quirk rather than
// the bug it is, and no other assertion here would notice.
@ -336,7 +344,9 @@ if (r.cards && r.imgs + r.nophoto !== r.cards) {
fails.push(`${r.cards} people cards but ${r.imgs} photo + ${r.nophoto} typographic = `
+ `${r.imgs + r.nophoto} — every card must be exactly one of the two`)
}
const expectPhotos = process.env.PACKET_EXPECT_PHOTOS !== '0'
// The packet's own stamp is authoritative; the env var stays as an override for
// artefacts generated before data-packet-build existed.
const expectPhotos = process.env.PACKET_EXPECT_PHOTOS !== '0' && !/\bnophotos\b/.test(build)
if (r.cards && expectPhotos && r.imgs === 0) {
fails.push(`${r.cards} people cards and NOT ONE photo — the packet spec is "20 per page `
+ 'WITH IMAGES", and every source record carries an image_url, so this is a skipped or '
@ -389,7 +399,7 @@ if (r.metacamp !== 2) fails.push(`${r.metacamp} metacamp.org attributions, expec
// poster html files against wt-packet's 28, so every packet generated there is degraded
// this way and nothing said so.
// Default to FAILING, so the degradation has to be DECLARED rather than discovered.
const expectMap = process.env.PACKET_EXPECT_MAP !== '0'
const expectMap = process.env.PACKET_EXPECT_MAP !== '0' && !/\bnomap\b/.test(build)
if (r.mapFaults.length && expectMap) {
fails.push(`map: ${r.mapFaults.join('; ')}`
+ (r.mapGeoPaths === -1
@ -562,6 +572,7 @@ if (r.ranges.length) {
+ 'cross-checks the card count against is missing')
}
console.log(` build : ${build || '(unstamped — generated before data-packet-build)'}`)
console.log(` sheets/PDF pages : ${r.sheets} / ${pages}`)
console.log(` printed artefact : ${(pdfBytes / 1024 / 1024).toFixed(1)}MB, ${pdfFonts} embedded font(s), `
+ `${pdfImages} image(s) for ${r.distinctImgSrc} distinct source(s)`)
@ -572,7 +583,7 @@ console.log(` Mark 5 in full : ${r.mark5Len} chars`
+ `${scriptureIsEnglish ? ' ⚠ UNTRANSLATED (English fallback)' : ''}`)
console.log(` metacamp.org : ${r.metacamp}`)
console.log(` map (page 1) : ${r.mapFaults.length
? `${r.mapFaults.join('; ')}${expectMap ? '' : ' [declared deliberate: PACKET_EXPECT_MAP=0]'}`
? `${r.mapFaults.join('; ')}${expectMap ? '' : ` [declared deliberate: ${/\bnomap\b/.test(build) ? 'data-packet-build="' + build + '"' : 'PACKET_EXPECT_MAP=0'}]`}`
: `${r.mapGeoPaths} geography paths, palette resolves, not black`}`)
console.log(` page size : ${boxes[0] ? boxes[0].join(' x ') : '?'} pt`
+ `${boxes.length ? ` (all ${boxes.length} pages, asserted US Letter)` : ''}`)

View file

@ -219,7 +219,23 @@ for (const z of zones) {
const { html, pageCount, withPhoto } = await buildPacket(z, groups, mapSvg);
const name = LANG === 'en' ? `${z.id}.html` : `${z.id}.${LANG}.html`;
writeFileSync(join(OUT, name), html);
// ★ STAMP HOW THIS PACKET WAS BUILT, INTO THE PACKET. out/packets/ is gitignored, so the
// sweep gate (check:packets-all) grades whatever .html files happen to be sitting there —
// an ACCIDENTAL set, not a current one. MEASURED 2026-09-24: doxa-maps-plugin-studio, which
// shares main with this worktree, had two packets from a NOPHOTOS=1 probe days old and the
// sweep was RED there, on artifacts nobody intended to ship. A fast layout-iteration build
// and a shippable one were byte-indistinguishable: the packet carried lang and dir and
// nothing else about its own provenance.
// The GENERATOR stamps this rather than the renderer, because packet-render.js is shared
// with the browser export path and must not know about CLI flags.
// ⚠ NOT A TIMESTAMP. An mtime rule would fail a correct tree — measured in this very
// session, packet-strings.json was 303s newer than the packets while being byte-identical
// to HEAD, because a probe restored it from backup. Record the INTENT, which is a fact
// about the build, not the clock.
const provenance = [NOPHOTOS ? 'nophotos' : null, mapMiss ? 'nomap' : null]
.filter(Boolean).join(' ') || 'full';
const stamped = html.replace('<html ', `<html data-packet-build="${provenance}" `);
writeFileSync(join(OUT, name), stamped);
made++;
if (mapMiss) mapless.push(z.id);
// ⚠ DO NOT PRINT A BARE ✓ FOR A PACKET MISSING ITS FIRST PAGE. The map one-pager is the