packet generator: say when page 1 has no map, instead of printing a tick
Some checks are pending
build-gate / build (push) Waiting to run

`✓ laos  95 groups  8 pages  0 photos` — for a packet whose first page is blank.
The page count stayed 8 because a blank sheet is still a sheet.

The map one-pager is lifted from out/posters/<zone>.html, which a separate
manual step writes. When it is absent packet.mjs deliberately still builds, and
that fallback is right — but it was silent. The empty-groups branch three lines
above already warns and skips; the map branch had the same shape and no warning.

A gate catching this is not the same as the tool reporting it. The gate added
last tick runs later, optionally, and only when someone invokes it. The
generator prints its tick at the moment of production, which is the one output
an operator is guaranteed to see.

Now it prints ⚠ with the reason inline and repeats it in an end-of-run summary
naming every affected zone and the fix — a per-line mark scrolls off in a
27-zone run, and this is the one fault that cannot be seen by counting pages.

Both silent branches are covered, distinctly: a missing poster reports "no
out/posters/laos.html"; a poster that exists but carries no <svg> — a truncated
generate run — reports "out/posters/laos.html has no <svg>". An operator can
tell "never generated" from "generation broke".

Also adds the `packets` script. Its absence is why the posters -> packets
dependency was never expressible anywhere: packet generation was only ever a
hand-typed node invocation, so nothing could state what it reads.

Measured in both repos. doxa-maps-plugin-studio has 0 generated posters against
wt-packet's 28 — out/ is gitignored in both, so posters are build artifacts and
this is a missing build step, not a repo defect. A fresh studio run now warns;
wt-packet prints a clean tick. The size difference corroborates: 0.4 MB with the
map, 0.1 MB without.

i18n re-verified end to end since this touches the generator: a partial locale
block (2 strings, 3/12 points, 4/12 rhythms) renders the wheel 4 translated and
8 English, 12 point rows with 4 translated, 0 blanks, clean tick. All 11 locales
regenerated with photos — packets-all 11/11, each 12/12 wheel labels, 12/12
points, 13 geography paths. Wave 34/34.
This commit is contained in:
doxa-maps-88655c70 2026-09-24 13:53:52 +00:00
parent 0a2fd134ec
commit e130cb672a
2 changed files with 31 additions and 2 deletions

View file

@ -174,6 +174,7 @@ const zones = allZones().filter((z) => !ONLY || z.id === ONLY);
if (!zones.length) { console.error(`[packet] no zone matches ONLY=${ONLY}`); process.exit(1); }
let made = 0;
const mapless = []; // zones whose page 1 rendered without its map
for (const z of zones) {
let slugs = [];
try {
@ -190,6 +191,10 @@ for (const z of zones) {
// it the packet still builds (cover page without the map) rather than failing.
const posterPath = join(__dirname, 'out', 'posters', `${z.id}.html`);
let mapSvg = '';
let mapMiss = null; // why page 1 has no map, if it has none
if (!existsSync(posterPath)) {
mapMiss = 'no out/posters/' + z.id + '.html';
}
if (existsSync(posterPath)) {
const poster = readFileSync(posterPath, 'utf8');
const m = poster.match(/<svg[\s\S]*?<\/svg>/);
@ -206,13 +211,36 @@ for (const z of zones) {
if (m) mapSvg = `<style>${mapCss(poster)}</style>
<div style="position:absolute;left:0;top:250px;width:${W}px;
transform:scale(0.62);transform-origin:top center">${m[0]}</div>`;
// The poster EXISTS but carries no <svg> — a truncated or failed generate run. Same
// outcome as a missing file (page 1 is blank) and it was equally silent, so it is
// reported the same way rather than left as the one unexplained branch.
else mapMiss = `out/posters/${z.id}.html has no <svg>`;
}
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);
made++;
console.log(` ✓ ${z.id.padEnd(34)} ${String(groups.length).padStart(4)} groups `
+ `${pageCount} pages ${withPhoto} photos ${(html.length / 1024 / 1024).toFixed(1)} MB`);
if (mapMiss) mapless.push(z.id);
// ⚠ DO NOT PRINT A BARE ✓ FOR A PACKET MISSING ITS FIRST PAGE. The map one-pager is the
// first line of the packet spec, and the fallback above is deliberate — but it was also
// SILENT: this printed "✓ laos 95 groups 8 pages" for a packet whose page 1 is empty,
// with the page count still 8 because the blank sheet is still a sheet. Measured
// 2026-09-24 in doxa-maps-plugin-studio, which has 0 generated posters: every packet
// produced there was degraded this way and the only clue was the packet itself.
// The empty-groups branch a few lines above already warns and skips; this branch has the
// same shape and had no warning, which is the whole defect.
console.log(` ${mapMiss ? '⚠' : '✓'} ${z.id.padEnd(34)} ${String(groups.length).padStart(4)} groups `
+ `${pageCount} pages ${withPhoto} photos ${(html.length / 1024 / 1024).toFixed(1)} MB`
+ (mapMiss ? ` NO MAP on page 1 (${mapMiss})` : ''));
}
console.log(`\n${made} packet(s) → ${OUT}${ONLY ? ` (ONLY=${ONLY})` : ''}${LANG !== 'en' ? ` [${LANG}]` : ''}`);
// Repeat it at the END too. A per-line ⚠ scrolls off in a 27-zone run; the summary is the
// part an operator actually reads, and this is the one fault that cannot be seen by
// counting pages.
if (mapless.length) {
console.warn(`\n⚠ ${mapless.length} of ${made} packet(s) have NO MAP on page 1: ${mapless.join(', ')}`);
console.warn(' The map one-pager is lifted from out/posters/<zone>.html, which `bun run posters`');
console.warn(' generates. Without it the packet still builds — page 1 is simply blank.');
console.warn(' Fix: bun run posters (then re-run this script)');
}

View file

@ -34,6 +34,7 @@
"posters:gallery": "cd library/scripts/posters && node build-gallery.mjs && node build-hd-gallery.mjs",
"posters:export": "cd library/scripts/posters && node export-public.mjs",
"posters:previews": "cd library/scripts/posters && python3 make-previews.py",
"packets": "cd library/scripts/posters && VITE_API_BASE_URL=https://pray.doxa.life node packet.mjs # the prayer packets. READS out/posters/<zone>.html for page 1's map, which `bun run posters` writes — without it the packet still builds and page 1 is simply BLANK, so the script now warns per-zone AND in its summary. PACKET_LANG=<loc> for one language, ONLY=<zoneKey> for one zone, NOPHOTOS=1 to skip the photo fetch.",
"token": "node internal/write-token.mjs",
"smoke": "node internal/smoke.mjs",
"smoke:embeds": "node internal/embed-smoke.mjs",