From bfc1e0029bb2fceb299322935c529405b5f23166 Mon Sep 17 00:00:00 2001 From: doxa-maps-88655c70 <88627970+doxa-maps-88655c70@users.noreply.github.com> Date: Thu, 24 Sep 2026 15:53:58 +0000 Subject: [PATCH] gates: two unwired checks were red on main, and nothing could tell us MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Censused the 67 gate scripts that are in no wave. The framing I had been using — "coverage we are not getting" — was wrong in the more serious direction. Two of them were FAILING on main right now. The gates exist, work, and had found something; because nothing ran them the findings sat silently. check:legend-source, a real product finding: the live corpus moved 2108 -> 2107 and one group crossed Unengaged -> Engaged, so the legend baseline was describing a dataset that no longer exists. Confirmed 2107 independently against the live endpoint rather than trusting the gate's own reading. Updated the baseline following the procedure its header requires, repeated rather than assumed: grepping the six figures across plugin-designs/ and library/ finds them only in explanatory comments, so no shipping code hardcodes a corpus total and only the baseline lagged. Mutation-tested — an off-by-8 baseline fails. Recorded that this is the third reading, that it drifts in both directions, and that it must NOT be widened into a range: the noise is the signal that the numbers on the page are live rather than asserted. check:verification-tags asks whether a human ever looked at a done card. 98 could not answer. 24 of those were mine, and they are now tagged agent/state — honestly agent rather than human, and state rather than visual, because every one was verified by measuring rendered DOM, PDF bytes and request tallies, never by a human looking at pixels. The other 74 belong to other agents and the Driver; guessing their provenance would defeat the gate, so it stays unwired. Six further gates were green and self-contained and are now wired: locale reachability, url resolution, manifest coverage, the palette registry, and the legend/source comparison. gates:post 37 -> 42; wave membership 43 -> 50 of 106. One I nearly wired wrong. check:dxlife passes standalone but exits 2 by documented contract — 0 unchanged, 1 drift, 2 clone not found — and the wave runner grades any non-zero as failure, so a checkout without the sibling clone would go red on a precondition. Caught in the fresh-checkout run, unwired, with the reason recorded on the script so the next census does not re-add it. "Can it run" and "is it wave-safe" are different questions. 42/42 with servers up; 31/42 passed and 7 SKIPPED on a fresh checkout, every skip named. --- .../README-SECTION-3-cli-commands.md | 2 +- internal/check-legend-matches-source.mjs | 20 +++++++++++++++++-- package.json | 4 ++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/documentation/README-SECTION-3-cli-commands.md b/documentation/README-SECTION-3-cli-commands.md index a966e66fd..d49c35161 100644 --- a/documentation/README-SECTION-3-cli-commands.md +++ b/documentation/README-SECTION-3-cli-commands.md @@ -49,7 +49,7 @@ The scripts these commands are grounded in (from this branch's `package.json`): "build": "bash internal/build-lock.sh", "build:unlocked": "bun run gates:pre && bun run library-index && bun run check:library-index && bun run clean && bun run token && bun run build:bundles && bun run tree && bun run gates:post && bun run gallery", "gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:wp-embed check:doc-scripts", -"gates:post": "node internal/run-parallel.mjs --label=post -- check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch", +"gates:post": "node internal/run-parallel.mjs --label=post -- check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:legend-source", "build:bundles": "node internal/run-parallel.mjs --bundles", // the 22 vite builds, 4 at a time "preview": "bun x vite preview", "serve": "node internal/serve-strict.mjs", // 404s a directory, like production diff --git a/internal/check-legend-matches-source.mjs b/internal/check-legend-matches-source.mjs index 90e639c55..c556baf78 100644 --- a/internal/check-legend-matches-source.mjs +++ b/internal/check-legend-matches-source.mjs @@ -46,9 +46,25 @@ const UA = 'Mozilla/5.0 (compatible; doxa-maps-gate/1.0)' // the number ONLY inside explanatory comments — no shipping code hardcodes a corpus total. Every // legend and KPI computes from the rows it loaded. The corpus moving 2107 -> 2108 therefore changes // what the maps DISPLAY automatically, and only this baseline lagged. +// Re-measured 2026-09-24 (THIRD reading). The corpus has now moved 2107 -> 2108 -> 2107, and one +// group crossed from Unengaged to Engaged: 2081/27 -> 2079/28. Sums check (2079 + 28 = 2107). +// +// ★ THE SAME SAFETY CHECK WAS REPEATED, not assumed from last time. The gate's own failure text +// forbids updating this baseline alone, because that silences the check while leaving the maps +// wrong. So the maps were checked FIRST: grepping 2107/2108/2,107/2,108/2081/2079 across +// plugin-designs/ and library/ finds them ONLY inside explanatory comments — no shipping code +// hardcodes a corpus total, every legend and KPI computes from the rows it loaded. The corpus +// figure was also confirmed INDEPENDENTLY against the live endpoint (2107 rows) rather than +// taken from this gate's own reading. +// +// ⚠ THIS DRIFTS, AND THAT IS THE POINT. Three readings in two days, moving in both directions. +// A failure here is normally UPSTREAM DATA MOVING, not a regression — but it must still be seen, +// because the one thing it would also catch is a legend that stopped computing and started +// asserting. Do not widen this into a range to stop the noise: the noise IS the signal that the +// numbers on the page are live. const EXPECT = { - total: 2108, - engagement: { Unengaged: 2081, Engaged: 27 }, + total: 2107, + engagement: { Unengaged: 2079, Engaged: 28 }, adoptedPct: 1, } diff --git a/package.json b/package.json index 7962d5c0c..2a92f4f43 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "deploy:dev": "bun run build && node internal/sync-dropin.mjs --from-config --prune # destination + allowlist come from vite.doxa-maps-build-config.json (dropin.dest / dropin.include); DOXA_DROPIN_DEST= overrides the destination", "check:public-hygiene": "node internal/public-hygiene-check.mjs", "check:label-joins": "node internal/check-label-joins.mjs", - "check:dxlife": "node internal/check-dxlife-untouched.mjs", + "check:dxlife": "node internal/check-dxlife-untouched.mjs # ⚠ NOT WAVE-SAFE, deliberately unwired: exit codes 0/1/2 are a DOCUMENTED contract (0 unchanged · 1 drift · 2 clone not found), and run-parallel grades any non-zero as FAILURE — so a checkout without the sibling /root-dx-life clone goes red on a precondition. Changing the contract could break another consumer; hand-run it, or give it a recognised SKIP first.", "check:facet-cascade": "node internal/check-facet-cascade.mjs # a child facet selection must not outlive its parent: clearing a parent facet must cascade-clear its descendants on EVERY clear path. Asserts on RESULT COUNTS, not DOM — an orphaned child silently ANDs the map to empty. run check:facet-cascade:self-test for the NEGATIVE test (reverts the cascade and requires this gate to FAIL).", "check:facet-cascade:self-test": "node internal/check-facet-cascade.mjs --self-test", "check:facet-one-field": "node internal/check-facet-one-field.mjs # ONE AXIS = ONE CONTROL-REACHABLE FACET. Asserts on the PARTITION each facet induces over the rows, NOT on which r. it reads: three fields (religion, primary_language, rop1) are legitimately read by two facets each as tier-1/tier-2 pairs, so a gate on duplicate field READS would fail correct code. Scoped to facets a user can actually select (rail + dropdowns + charts), because a search-only projection sharing a partition is fine until it becomes a control. run check:facet-one-field:self-test for the NEGATIVE test.", @@ -157,7 +157,7 @@ "check:doc-scripts:self-test": "node internal/check-doc-quotes-scripts.mjs --self-test", "check:legend-source": "node internal/check-legend-matches-source.mjs # the numbers the maps SHOW must match the numbers the live SOURCE has. Every other gate checks INTERNAL consistency — a page can agree with itself perfectly while its legend has stopped describing reality. Offline SKIPs (exit 0); only a reachable endpoint that disagrees fails.", "gates:pre": "node internal/run-parallel.mjs --label=pre -- check:template-literal check:mjs-parse check:sfc-parse check:tab-radius check:synced-css check:wp-embed check:doc-scripts", - "gates:post": "node internal/run-parallel.mjs --label=post -- check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch", + "gates:post": "node internal/run-parallel.mjs --label=post -- check:drop check:parse verify check:sri check:public-hygiene check:label-joins check:facet-cascade check:facet-one-field check:heartbeat check:kpi-unit check:composite-reg check:descriptions check:no-abs-paths check:no-codename check:dep-names check:composite-srcs check:embed-targets check:nested-token check:i18n-parity check:tabs-localized check:packets-all check:packet-strings-parity check:zone-census check:share-policy check:library-index check:csv-shape check:capability-citations check:catalog-identity check:no-orphan-gates check:quoted-scripts check:done-evidence check:script-flags check:backlog-status check:capability-exemplars check:packet-fixture check:packet-browser check:provider-switch check:locale-reachable check:url-resolver check:manifest-covers check:one-palette check:legend-source", "build:bundles": "node internal/run-parallel.mjs --bundles", "check:maplibre-drops": "node internal/check-maplibre-layer-drops.mjs", "check:tabs-localized": "node internal/check-tabs-localized.mjs # a chosen locale must CHANGE the rendered tab text. check:i18n-parity compares catalogs to each OTHER, so it cannot tell a shipped translation from a dead one; tabs.json is reached only via a DYNAMIC key (SemanticTreeLegend.vue:40), invisible to any grep. Renders it and reads the result.",