Some checks are pending
build-gate / build (push) Waiting to run
Found by the folder-scan agent and verified by simulation. This gate resolves ROOT at depth 2 while all 40 sibling gates use depth 1, because the backlogs it reads live in _qa-cycle/ - agentic scratch OUTSIDE the repo, tracked by no git repository and never cloned with the studio. On this machine _qa-cycle happens to sit beside the repo, so it passes. An inheritor who clones only the studio got something worse than a failure: it printed "0 card(s) across 3 backlog(s), 0 done ... PASS" and exited 0. A vacuously green gate certifies nothing while looking healthy. Now it detects the absent directory and SKIPs with an explanation, still exiting 0 so it cannot break an inheritor's build. Negative-tested both ways: 274 cards still checked here, honest SKIP in a bare clone.
116 lines
5.3 KiB
JavaScript
116 lines
5.3 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* check-done-evidence.mjs — a card may not reach status:done with NO evidence.
|
|
*
|
|
* WHY (2026-09-21)
|
|
* Two quality cards were status:done with an EMPTY bluf. Both turned out genuinely done — but
|
|
* only because I opened a browser and read the source. A board-wide count then found 18 done
|
|
* cards carrying NEITHER a bluf NOR any fields: a status word and nothing else.
|
|
*
|
|
* ★ THE HAND-OFF IS THE POINT. The Doxa team will read this board to learn what exists. A card
|
|
* reading "done" with no evidence cannot be reconciled against the code at all — they cannot
|
|
* tell a shipped deliverable from an abandoned one someone closed. This flight found SIX cards
|
|
* whose stated status disagreed with reality; an evidence-less card is the unfalsifiable case.
|
|
*
|
|
* WHAT COUNTS AS EVIDENCE (deliberately generous — this is a floor, not a style guide)
|
|
* EITHER a non-empty `bluf` OR a non-empty `fields` object. Both are free to write at the moment
|
|
* the work is fresh, which is the only moment the evidence is cheap and accurate.
|
|
*
|
|
* BASELINE, NOT A CLIFF
|
|
* The 10 pre-existing offenders are allow-listed by id. A gate that fails on day one for history
|
|
* nobody can fix gets disabled, and then it protects nothing. This fails ONLY on a NEW one — the
|
|
* same shape as check-label-joins, which allow-lists two known occurrences.
|
|
* ★ TO CLEAR AN ENTRY: add real evidence to the card, then delete its id here. The list should
|
|
* only ever shrink; a PR that grows it is doing the thing this gate exists to stop.
|
|
*
|
|
* NEGATIVE-TESTED: adding a fresh done card with no bluf/fields makes this FAIL naming it,
|
|
* EXIT=1; removing it returns PASS.
|
|
*/
|
|
import { readFileSync, existsSync } from 'node:fs'
|
|
import { resolve, dirname, join } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
// ★ DEPTH 2, NOT 1 — and that is deliberate but load-bearing. Every sibling gate in internal/
|
|
// resolves ROOT one level up (the repo). This one goes TWO levels up, because the backlogs it
|
|
// checks live in `_qa-cycle/`, which is AGENTIC SCRATCH OUTSIDE THE REPO — tracked by no git
|
|
// repository, and never cloned with the studio.
|
|
//
|
|
// THE HAND-OFF DEFECT THIS GUARD FIXES (measured 2026-09-22). On this machine _qa-cycle happens
|
|
// to sit beside the repo, so the gate passes. An inheritor who clones ONLY the studio has no
|
|
// sibling _qa-cycle — and the gate did NOT fail there. It printed
|
|
// "0 card(s) across 3 backlog(s), 0 done" ... "PASS"
|
|
// and exited 0. A VACUOUSLY GREEN gate is worse than no gate: it certifies nothing while looking
|
|
// healthy, which is exactly the failure shape this repo keeps re-learning.
|
|
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..')
|
|
const QA = join(ROOT, '_qa-cycle')
|
|
|
|
if (!existsSync(QA)) {
|
|
console.log('DONE-EVIDENCE: SKIP — no _qa-cycle/ beside this repo, so there are no backlogs to check.')
|
|
console.log(' This gate reads the AGENTIC BACKLOGS, which live outside the repo and are not cloned')
|
|
console.log(' with it. Absence is expected for anyone who checked out the studio on its own; it is')
|
|
console.log(' NOT a pass. Run it from a workspace that has _qa-cycle/ as a sibling of this repo.')
|
|
process.exit(0)
|
|
}
|
|
|
|
const BACKLOGS = [
|
|
'01-ideation-research/backlog.json',
|
|
'02-builder-cycle/backlog.json',
|
|
'03-deployment-cycle/backlog.json',
|
|
]
|
|
|
|
// Pre-existing, from before the rule. Verified 2026-09-21. SHRINK ONLY.
|
|
const BASELINE = new Set([
|
|
'BUILD-AGENT-SKILLS',
|
|
'BUILD-CAP1-SINGLE-ELEMENT-SHOWCASE',
|
|
'BUILD-PLUGIN-STUDIO-VUE-SPA',
|
|
'BUILD-README-CLI-COMMANDS',
|
|
'BUILD-README-DEDUP-BLUF',
|
|
'BUILD-README-SCREENSHOT-MATRIX',
|
|
'BUILD-RENAME-PLUGIN-DESIGNS',
|
|
'BUILD-RESTORE-MOBILE-SPA',
|
|
'BUILD-STARTER-TEMPLATE',
|
|
'BUILD-UNIFIED-RESEARCH-DASHBOARD',
|
|
])
|
|
|
|
let checked = 0, done = 0
|
|
const offenders = []
|
|
const staleBaseline = []
|
|
|
|
for (const rel of BACKLOGS) {
|
|
const p = join(QA, rel)
|
|
if (!existsSync(p)) continue
|
|
let cards
|
|
try { cards = JSON.parse(readFileSync(p, 'utf8')).cards || [] } catch { continue }
|
|
for (const c of cards) {
|
|
checked++
|
|
if (c.status !== 'done') continue
|
|
done++
|
|
const hasBluf = String(c.bluf || '').trim().length > 0
|
|
const hasFields = c.fields && Object.keys(c.fields).length > 0
|
|
if (hasBluf || hasFields) {
|
|
// A baselined id that now HAS evidence is stale — report it so the list shrinks.
|
|
if (BASELINE.has(c.id)) staleBaseline.push(`${c.id} (${rel})`)
|
|
continue
|
|
}
|
|
if (BASELINE.has(c.id)) continue
|
|
offenders.push(`${c.id} (${rel})`)
|
|
}
|
|
}
|
|
|
|
console.log(`DONE-EVIDENCE: ${checked} card(s) across ${BACKLOGS.length} backlog(s), ${done} done, ` +
|
|
`${BASELINE.size} baselined.`)
|
|
for (const s of staleBaseline) {
|
|
console.log(` i BASELINE NOW STALE — ${s} has evidence; remove its id from check-done-evidence.mjs`)
|
|
}
|
|
|
|
if (offenders.length) {
|
|
console.error(`\nDONE-EVIDENCE: FAIL — ${offenders.length} card(s) are done with NO bluf and NO fields:`)
|
|
for (const o of offenders) console.error(` ✗ ${o}`)
|
|
console.error(
|
|
'\n Add ONE of: a `bluf` saying what was done, or a `fields` entry naming the artifact and how\n' +
|
|
' it was verified. Write it now, while the work is fresh — reconstructing it later produces a\n' +
|
|
' plausible story indistinguishable from a real record.'
|
|
)
|
|
process.exit(1)
|
|
}
|
|
console.log('DONE-EVIDENCE: PASS — every newly-done card carries a bluf or fields.')
|