Two more gates join gates:pre honestly: dxlife walks only the clones, and the calendar stays on demand
check:dxlife walked the whole root-dx-life umbrella, so git worktrees of doxa-life (wt-rg-*) and a dangling one (research-map-clean, whose .git points at a path that no longer exists) read as +4,524 "added" files and held it red while the clones were untouched. It now skips any directory whose .git is a FILE beginning `gitdir:` (keyed on that file, not on `git worktree list`, which cannot see the dangling one) and .cache with the other generated dirs, and compares the baseline only within that scope. Result against the 2026-09-20 baseline: 2,844 clone files, +0 -0 ~0. So the law has held since that date, and the baseline stays as it is: re-taking it would reset the evidence. Nothing is deleted; research-map-clean may hold the only copy of its work. With no clone on the machine it reports NOT MEASURED (75) instead of exit 2, so chaining it cannot fail other machines. check:coverage-consistent fails when the oldest verified link is over 45 days old: its verdict depends on the calendar, and chained it would turn gates:pre red on 2026-11-08 with no change (regions measured the margin). Every other clause depends on code and data and is worth running on every landing, so gates:pre runs check:coverage-consistent:chained (the age knob set out of reach). The plain script is declared on demand as the freshness run, with that reason. Its poison self-test still proves every clause can fail. gates:pre 94/0/2 of 96. The orphan baseline shrinks to 105; 7 checks are declared on demand.
This commit is contained in:
parent
5520587417
commit
b142fe1178
4 changed files with 29 additions and 8 deletions
|
|
@ -24,7 +24,17 @@
|
|||
* USAGE
|
||||
* node internal/check-dxlife-untouched.mjs # create or verify the baseline
|
||||
* DXLIFE=/path/to/clone node internal/check-dxlife-untouched.mjs
|
||||
* EXIT 0 unchanged (or baseline created) · 1 drift detected · 2 clone not found
|
||||
* ★ WHAT IT WALKS: the DX Life CLONES, not every directory under the umbrella (2026-09-29). root-dx-life
|
||||
* also holds git WORKTREES of doxa-life (wt-rg-*, today's research-gallery branches) and a DANGLING
|
||||
* one (research-map-clean, whose .git points at a path that no longer exists). Walking them counted
|
||||
* sanctioned work and generated output as "added" and held this gate red, while the clones themselves
|
||||
* were untouched. A directory whose .git is a FILE beginning `gitdir:` is a worktree and is skipped,
|
||||
* keyed on that file rather than on `git worktree list`, which does not show a dangling one. Skipped,
|
||||
* never deleted: research-map-clean's parent repo is gone, so it may hold the only copy of its work.
|
||||
* Generated dirs are skipped too, and the baseline is compared only WITHIN this scope, so narrowing
|
||||
* the scope reads as no change rather than as thousands of removals.
|
||||
*
|
||||
* EXIT 0 unchanged (or baseline created) · 1 drift detected · 75 not measured (no clone on this machine)
|
||||
*/
|
||||
import { readdirSync, statSync, existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs'
|
||||
import { join, resolve, dirname } from 'node:path'
|
||||
|
|
@ -35,13 +45,19 @@ const CLONE = process.env.DXLIFE || resolve(HERE, '..', '..', '..', 'root-dx-lif
|
|||
const STORE = resolve(HERE, '..', '..', '_qa-cycle', '_scratch-coordinator', 'dxlife-baseline.json')
|
||||
|
||||
if (!existsSync(CLONE)) {
|
||||
console.error(`DXLIFE-UNTOUCHED: clone not found at ${CLONE} — nothing to check.`)
|
||||
process.exit(2)
|
||||
// Not measured, not failed: a machine without the DX Life umbrella has nothing this law covers.
|
||||
console.log(`DXLIFE-UNTOUCHED: NOT MEASURED — no DX Life clone at ${CLONE} on this machine.`)
|
||||
process.exit(75)
|
||||
}
|
||||
|
||||
const SKIP = new Set(['node_modules', '.git', '.output', '.nuxt', 'dist'])
|
||||
const SKIP = new Set(['node_modules', '.git', '.output', '.nuxt', '.cache', 'dist'])
|
||||
const isWorktree = (dir) => {
|
||||
try { const g = join(dir, '.git'); return statSync(g).isFile() && readFileSync(g, 'utf8').startsWith('gitdir:') } catch { return false }
|
||||
}
|
||||
const worktrees = [] // relative paths skipped as worktrees, reported below
|
||||
const snap = {}
|
||||
;(function walk(dir) {
|
||||
if (dir !== CLONE && isWorktree(dir)) { worktrees.push(dir.slice(CLONE.length + 1)); return }
|
||||
let entries
|
||||
try { entries = readdirSync(dir, { withFileTypes: true }) } catch { return }
|
||||
for (const e of entries) {
|
||||
|
|
@ -65,7 +81,10 @@ if (!existsSync(STORE)) {
|
|||
process.exit(0)
|
||||
}
|
||||
|
||||
const base = JSON.parse(readFileSync(STORE, 'utf8')).files
|
||||
// Compare within the walked scope: a baseline entry under a skipped worktree or generated dir is out of scope.
|
||||
const inScope = (rel) => !worktrees.some((w) => rel === w || rel.startsWith(w + '/')) && !rel.split('/').some((seg) => SKIP.has(seg))
|
||||
const base = Object.fromEntries(Object.entries(JSON.parse(readFileSync(STORE, 'utf8')).files).filter(([k]) => inScope(k)))
|
||||
console.log(`DXLIFE-UNTOUCHED: skipped ${worktrees.length} worktree(s) (a .git FILE): ${worktrees.join(', ') || 'none'}`)
|
||||
const added = Object.keys(snap).filter((k) => !(k in base))
|
||||
const removed = Object.keys(base).filter((k) => !(k in snap))
|
||||
const changed = Object.keys(snap).filter((k) => k in base && base[k] !== snap[k])
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"check:published": "fetches the live published host (needs DOXA_PUBLISHED_BASE): a property of the deployment, not of this tree",
|
||||
"check:composite": "rebuilds every bundle with vite to compare its bytes, minutes long by design; on every landing the precondition's fresh step rebuilds the composites that landing reaches",
|
||||
"check:hardcoded-english": "report-only while its findings are triaged (97 user-visible English strings on 2026-09-29); chaining it now would hold every gate red. Its :self-test IS chained, so the matcher stays proven",
|
||||
"check:shot-fonts": "checks that the MACHINE taking README screenshots can render emoji (fc-list): a property of that box, not of the product, so it runs before a screenshot session, not in the gates"
|
||||
"check:shot-fonts": "checks that the MACHINE taking README screenshots can render emoji (fc-list): a property of that box, not of the product, so it runs before a screenshot session, not in the gates",
|
||||
"check:coverage-consistent": "the full run includes clause L, the age of the oldest link verification (45 days), which depends on the CALENDAR: chained, it would turn gates red on a date (first 2026-11-08) with no code change. Run it with the link re-verification; every other clause runs in gates:pre as check:coverage-consistent:chained"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
"check:deck-click",
|
||||
"check:dev-assets",
|
||||
"check:doc-scripts:self-test",
|
||||
"check:dxlife",
|
||||
"check:element-census-vs-live",
|
||||
"check:embed-contract",
|
||||
"check:embed-contract:poison",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
"check:composites-mount": "node internal/check-composites-mount.mjs",
|
||||
"check:country-names": "node internal/check-country-names.mjs",
|
||||
"check:coverage-consistent": "node internal/check-gospel-coverage-consistent.mjs",
|
||||
"check:coverage-consistent:chained": "VERIFY_MAX_AGE_DAYS=36500 node internal/check-gospel-coverage-consistent.mjs",
|
||||
"check:coverage-consistent:poison": "node internal/check-gospel-coverage-consistent.mjs --poison",
|
||||
"check:csv-shape": "node internal/check-csv-export-shape.mjs",
|
||||
"check:csv-shape:self-test": "node internal/check-csv-export-shape.mjs --self-test",
|
||||
|
|
@ -330,7 +331,7 @@
|
|||
"fix:sri": "node internal/fix-sri.mjs",
|
||||
"gallery": "node internal/generate-gallery.mjs",
|
||||
"gates:post": "node internal/run-parallel.mjs --label=post -- check:capability-derived 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:zoning-declared 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:pin-dim-slider check:legend-source check:backlog-refs check:clustering-modes check:composite-embed-knobs check:composite-locale-leaks check:linked-vocabulary check:wp-embed check:dashboard-geocoder-provider check:leaf-engine-per-provider check:auto-engine check:engine-contract check:map-props-unit check:composites-mount check:composite-interop check:no-free-identifiers check:map-fly-reduced-motion check:mst-clustering check:list-prop-unit check:profiles-mount check:cdn-pins check:no-eval check:popup-escaping check:i18n-treeshake check:pinned-umd check:one-palette:self-test check:no-cross-plugin-imports check:no-cross-plugin-imports:self-test check:map-attribution check:studio-rail check:approx-pin-note check:embedder-keys check:provider-for check:legend-logical-insets check:legend-logical-insets:self-test check:optional-t-equivalence check:text-highlight check:religion-label-not-value check:i18n-stopped-scope check:no-silent-engine check:no-silent-engine:self-test check:regions-uses-library check:shadow-css-parses check:square-map-controls check:preset-config-merge check:instance-config-keys check:frozen-labels check:plural-forms check:region-fill-repaint check:poster-print-fallback check:posters-exportable check:country-names check:reduced-motion check:no-dead-library check:universal-props check:readme-size check:tree-relative check:snippet-engine check:snippet-engine:self-test check:embed-base check:link-coverage check:no-public-binds check:shared-reached check:tree-weight",
|
||||
"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:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-new-driver-references check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts",
|
||||
"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:doc-scripts check:setup-order check:package-json-unique-keys check:no-repo-pb-hooks check:no-credentials-omit check:no-credentials-omit:self-test check:shadow-reduced-motion check:poster-action-print check:targets check:targets:self-test check:browser-gates-guarded check:pin-selection check:facet-shape check:hardcoded-english:self-test check:list-selection check:selector-fields check:upg-zone-joins check:gospel-set-charts check:gospel-set-fit check:describe-group check:one-scroll-container check:packet-charts check:composite-token-url check:language-tree-layout check:i18n-keys-exist check:i18n-never-translated check:packet-variants check:profile-table check:element-bootstrap check:embed-url-absolute check:frame-target check:language-tree-model check:map-menu-anchor check:selection-kinds check:tiered-points check:whiteboard-projection check:mercator-slice check:ring-vocabulary check:map-props-unit check:mobile-gating check:provide-shadowing check:legend-top-offset check:cdn-cache check:plural-forms check:select-zone-race check:script-registry check:map-control-overlap check:poster-attribution check:board-map-options check:gospel-whiteboard check:packet-map-missing-loud check:poster-layout-optin check:ported-globals-identical check:ring-bands check:packet-a1-structure check:zone-title-tier-a check:evangelical-share check:map-labels check:packet-offline check:help-reachable check:gated-not-in-tree check:window-globals-released check:legend-data-contract check:no-new-driver-references check:poster-fetch-guard check:people-group-records check:sentence-slots check:popup-href-escaped check:viewport-meta check:viewport-meta:self-test check:label-key-coverage check:poster-browser-equiv check:poster-render-pinned check:host-locale-fetch check:print-frame-reclaim check:click-sheet check:coverage-consistent:chained check:dashboard-filter-a11y check:filter-multivalue check:hover-channel check:kit-options-reachable check:layout-key-namespaced check:legend-mirrors check:map-unmount-race check:no-conflict-markers check:series-isolation check:snapshot-guard check:stale-sweep-claim check:surfaces-read-the-tier check:translator-contacts check:dxlife",
|
||||
"gen:iso3-to-alpha2": "node library/scripts/generate-iso3-to-alpha2.mjs",
|
||||
"internet:adm1": "node plugin-designs/cities-internet/scripts/build-adm1-internet.mjs",
|
||||
"library-index": "node internal/generate-library-index.mjs",
|
||||
|
|
@ -393,6 +394,7 @@
|
|||
"check:auto-engine": "BROWSER. The default engine (auto) is MapLibre, token or not: on one map leaf, no token AND a host token alone both draw with MapLibre, paint, and make zero api.mapbox.com requests; ?provider=mapbox with that token draws Mapbox and carries it. In gates:post, NOT MEASURED outside the browser slot.",
|
||||
"check:no-new-driver-references": "shipped text (library, plugins, internal, docs, skills, README) neither quotes nor cites the Driver: a line that refers to the Driver and is not in internal/fixtures/driver-references-baseline.json FAILS. Forward-only: the baseline, keyed by file and line text with counts (not line numbers), may only shrink as owners sweep their areas (--shrink tidies it; it never grows). Matches the role word only, not a GPU or device driver. Node only.",
|
||||
"check:no-orphan-gates": "every gate-shaped internal/*.mjs is reachable or tagged @one-shot; and every check:/verify:/smoke: script is run by a standing chain, matched by EXACT name (gates:pre, gates:post, the browser suite GATES, the build, and what those bun run), or declared in internal/fixtures/on-demand-checks.json with its reason, or in the shrink-only baseline of the 2026-09-29 orphans. A chained :self-test does not chain its gate. --shrink / --init maintain the baseline.",
|
||||
"check:coverage-consistent:chained": "every clause of check:coverage-consistent EXCEPT the link-verification age (VERIFY_MAX_AGE_DAYS set out of reach): the chains run what depends on the code and data, and leave what depends on the calendar to the on-demand run, so the gates cannot turn red on a date with no change.",
|
||||
"check:viewport-meta": "every generated page declares <meta name=\"viewport\"> with width=device-width. Without it a browser lays the page out at the 980px desktop fallback even when the viewport is set to 390, so a mobile probe silently measures a viewport no phone has and still passes (measured: innerWidth 980 without the tag, 390 with it). width=device-width is required specifically, because a meta carrying only initial-scale still lays out at 980. Checked on the BUILT tree, not the generator template. Fixed-size print sheets are exempt by their stated shape (a .stage with a three-digit px width), and a page that must render at a fixed desktop width carries a viewport-meta-ok: reason. 8 self-tests. Source-only, headless.",
|
||||
"check:viewport-meta:self-test": "plants each check:viewport-meta violation - no meta at all, and a meta with initial-scale but no width=device-width - and confirms both are caught, that correct tags in either quote style or uppercase pass, and that the print-sheet exemption applies to a fixed-px stage but NOT to a responsive one.",
|
||||
"check:plural-forms": "the three things a CLDR pluralRules map can silently break. (1) FORM COUNT: a locale must author as many forms as it has CLDR categories or the pick clamps onto the wrong one (ru 4, ar 6). (2) THE tStatic PATH: seven bundles install no vue-i18n and read through tStatic, which does NO plural selection, so a pipe key there reaches the page as the literal \"a | b | c\". (3) ZERO: a locale with an explicit CLDR zero category must author it. Written as the precondition for installing the map, and kept as the guard after.",
|
||||
|
|
|
|||
Loading…
Reference in a new issue