Settled-tree gate: assert leaf-page count, which per-bundle builds silently delete
This commit is contained in:
parent
37b9a550f7
commit
edb935f46a
1 changed files with 24 additions and 2 deletions
|
|
@ -44,6 +44,7 @@ import { execSync } from 'node:child_process'
|
|||
const TREE = process.env.TREE || 'public/doxa-maps'
|
||||
const QUIET_SECONDS = Number(process.env.QUIET_SECONDS || 45)
|
||||
const MIN_DIRS = Number(process.env.MIN_DIRS || 17)
|
||||
const MIN_LEAVES = Number(process.env.MIN_LEAVES || 50)
|
||||
|
||||
if (!existsSync(TREE)) {
|
||||
console.error('SETTLED-TREE: FAIL — tree does not exist: ' + TREE + ' (a build may be mid-clean)')
|
||||
|
|
@ -80,13 +81,33 @@ let newest = 0
|
|||
// ── signal 3: shape ──────────────────────────────────────────────────────────
|
||||
const dirs = readdirSync(TREE, { withFileTypes: true }).filter((e) => e.isDirectory()).length
|
||||
|
||||
// ── signal 4: LEAF COUNT ─────────────────────────────────────────────────────
|
||||
// ★ ADDED because a per-bundle `BUNDLE=x bun x vite build` does something worse than leave a stale
|
||||
// SRI hash: it EMPTIES public/doxa-maps/ of every OTHER bundle's leaf index.html. An agent ran it
|
||||
// five times as a compile check and silently deleted 62 leaf pages across 18 bundles — including
|
||||
// the root index.html and manifest.json — in bundles it had never touched.
|
||||
// Nothing caught it. check:sri only compares a leaf's baked hash to its bundle, so a leaf that no
|
||||
// longer EXISTS is simply not checked — the gate passes because there is nothing left to disagree.
|
||||
// dirs alone does not catch it either: the directories survive, only their index.html vanishes.
|
||||
// (Full builds regenerate the leaves via `bun run tree`, which is why the damage kept healing
|
||||
// itself and stayed invisible.)
|
||||
const countLeaves = (dir) => {
|
||||
let n = 0
|
||||
for (const e of readdirSync(dir, { withFileTypes: true })) {
|
||||
if (e.isDirectory()) n += countLeaves(join(dir, e.name))
|
||||
else if (e.name === 'index.html') n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
const leaves = countLeaves(TREE)
|
||||
|
||||
const quietFor = newest ? Math.round((Date.now() - newest) / 1000) : Infinity
|
||||
const ok = builders === 0 && waiters === 0 && churn === 0 && dirs >= MIN_DIRS
|
||||
const ok = builders === 0 && waiters === 0 && churn === 0 && dirs >= MIN_DIRS && leaves >= MIN_LEAVES
|
||||
|
||||
console.log(
|
||||
'SETTLED-TREE: builders=' + builders + ' waiters=' + waiters +
|
||||
' churn(' + QUIET_SECONDS + 's)=' + churn +
|
||||
' dirs=' + dirs + '/' + MIN_DIRS +
|
||||
' dirs=' + dirs + '/' + MIN_DIRS + ' leaves=' + leaves + '/' + MIN_LEAVES +
|
||||
' quietFor=' + (quietFor === Infinity ? 'n/a' : quietFor + 's')
|
||||
)
|
||||
|
||||
|
|
@ -99,4 +120,5 @@ if (builders) console.error(' a build is HOLDING the lock — wait for it')
|
|||
if (waiters) console.error(' ' + waiters + ' build(s) QUEUED — each will re-run clean and rebuild everything; wait for the QUEUE to drain, not just the current build')
|
||||
if (churn) console.error(' ' + churn + ' file(s) written in the last ' + QUIET_SECONDS + 's — something is still writing')
|
||||
if (dirs < MIN_DIRS) console.error(' only ' + dirs + ' bundle dirs (expected >=' + MIN_DIRS + ') — likely mid-clean or a half-built tree')
|
||||
if (leaves < MIN_LEAVES) console.error(' only ' + leaves + ' leaf index.html (expected >=' + MIN_LEAVES + ') — a per-bundle `BUNDLE=x vite build` empties OTHER bundles\' leaves; run a FULL build to regenerate them')
|
||||
process.exit(1)
|
||||
|
|
|
|||
Loading…
Reference in a new issue