From 53fea6ee38c24094eec234d2783f95387ee1fbb6 Mon Sep 17 00:00:00 2001 From: doxa-maps-88655c70 <88627970+doxa-maps-88655c70@users.noreply.github.com> Date: Thu, 24 Sep 2026 13:23:57 +0000 Subject: [PATCH] packet gate: a prayer wheel with no words on it is not a prayer wheel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Blanking every wheel segment label and regenerating left check:packet-render reporting "12 segments, 11/11 rhythms, content intact — PASS" on a wheel that was a twelve-colour pie chart. Both printed numbers stayed true, which is why nothing caught it. The twelve segments are elements and survive their labels being removed. And the rhythm assertion searched document.body.innerText, where the SAME twelve rhythm words are rendered TWICE in every packet — once per wheel segment, once as a prayer-point heading. So "every rhythm word appears in the document" was satisfied by the points page alone and said nothing about the wheel. That is the self-consistency-vs-completeness family again, with a new twist: when two surfaces share one vocabulary, a document-wide string search cannot attribute a hit to the surface being asserted. The fix is scoping the query to the surface, not a better word list. wheelLabels counts the non-numeric runs inside the .wheel-title sheet's own svg, excluding the twelve "5" pips and the "5 minutes x 12" footer. It counts ELEMENTS, NOT DISTINCT WORDS, deliberately: `praise` and `praise_end` collapse to one word in English — which is exactly why the existing line reads 11/11 and not 12/12 — and that collision is locale-dependent, so a distinct-word count would be a different number per language. The Driver's spec names the wheel as one of the packet's four sections, so 12 labels is a contract. Mutated packet reports 0/12 and fails; restored reports 12/12. i18n verified at the rendered layer with a partial locale block (4 of 12 rhythms translated): the wheel painted four XX- labels with the other eight falling back to English and zero blank — and the split pair is visible, segment 1 English PRAISE beside segment 12 XX-PRAISE_END, which is what per-key resolution looks like. All 11 locales regenerated with 95 photos each: check:packets-all 11/11, every one 12/12 labelled, including RTL ar and Devanagari hi. Wave 34/34. --- internal/check-packet-render.mjs | 39 +++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/internal/check-packet-render.mjs b/internal/check-packet-render.mjs index 427c9e05f..766365d92 100644 --- a/internal/check-packet-render.mjs +++ b/internal/check-packet-render.mjs @@ -186,6 +186,32 @@ const r = await page.evaluate(() => { rhythms: RHYTHMS.filter((w) => body.includes(w)).length, rhythmsTotal: RHYTHMS.length, wheelSegs: document.querySelectorAll('svg path[fill^="hsl"]').length, + // ⚠ COUNT THE WHEEL'S OWN LABELS, SCOPED TO THE WHEEL SHEET. `rhythms` below + // searches document.body.innerText, and the SAME rhythm vocabulary is rendered + // TWICE — once per wheel segment, and once per prayer point (each point prints + // tr(p.rhythm) as its heading). So "every rhythm word appears in the document" is + // satisfied by the POINTS page alone and says nothing about the wheel. + // Measured 2026-09-24: blanking every wheel segment label left the gate reporting + // "12 segments, 11/11 rhythms, content intact — PASS" on a prayer wheel that was a + // twelve-colour pie chart with no words on it. Both numbers stayed true: the + // segments are elements and survive, and the words were still on the points + // page. The Driver's spec names the wheel as one of the packet's four sections. + // + // COUNT ELEMENTS, NOT DISTINCT WORDS. `order` renders 12 segments from 12 keys, but + // `praise` and `praise_end` collapse to the same word in English (which is why the + // line below prints 11/11, not 12/12) — and that collision is LOCALE-DEPENDENT. A + // distinct-word count would be a different number per language. + wheelLabels: (() => { + const title = document.querySelector('.wheel-title') + const sheet = title ? title.closest('.sheet') : null + const svg = sheet ? sheet.querySelector('svg') : null + if (!svg) return -1 + // The segment labels are the non-numeric runs: the pips are all "5" and + // the footer reads "5 x 12". + return [...svg.querySelectorAll('text')] + .map((t) => (t.textContent || '').trim()) + .filter((s) => s && !/^\d+$/.test(s) && !/^\d[\s\S]*×\s*12$/.test(s)).length + })(), // ⚠ NOT body.includes('Gadarenes'). That was an ENGLISH proper noun, so this // assertion would go RED on a correctly-translated packet and stays green today // only because every locale falls back to English for this key. The check and the @@ -268,6 +294,17 @@ if (r.cards && expectPhotos && r.imgs === 0) { + 'PACKET_EXPECT_PHOTOS=0 to declare this a deliberately photo-less packet.') } if (r.wheelSegs !== 12) fails.push(`prayer wheel has ${r.wheelSegs} segments, expected 12`) +// A SEGMENT WITHOUT ITS WORD IS A COLOUR. 12 labels, one per segment, on the wheel +// sheet itself — not merely somewhere in the document. +const WHEEL_LABELS = 12 +if (r.wheelLabels !== WHEEL_LABELS) { + fails.push(r.wheelLabels === -1 + ? 'no prayer-wheel svg found (no .wheel-title sheet) — the wheel page did not render' + : `the prayer wheel paints ${r.wheelLabels} segment label(s), expected ${WHEEL_LABELS} — ` + + 'the rhythm words are rendered TWICE in this packet (wheel segments and prayer-point ' + + 'headings), so the document-wide rhythm check below passes on the points page alone ' + + 'and cannot see an unlabelled wheel') +} if (r.rhythms !== r.rhythmsTotal) fails.push(`only ${r.rhythms}/${r.rhythmsTotal} prayer rhythms present`) // Mark 5:1-20 in full is a Driver requirement for point 4: "it actually lists out the // full scripture, because prayer point number four is actually read the word." The @@ -437,7 +474,7 @@ if (r.ranges.length) { console.log(` sheets/PDF pages : ${r.sheets} / ${pages}`) console.log(` people cards : ${r.cards} (${r.imgs} photos, ${r.nophoto} typographic)`) console.log(` prayer points : ${r.points} / ${POINTS_EXPECT}`) -console.log(` wheel segments : ${r.wheelSegs}`) +console.log(` wheel segments : ${r.wheelSegs} (${r.wheelLabels}/${WHEEL_LABELS} labelled)`) console.log(` Mark 5 in full : ${r.mark5Len} chars` + `${scriptureIsEnglish ? ' ⚠ UNTRANSLATED (English fallback)' : ''}`) console.log(` metacamp.org : ${r.metacamp}`)