clusterflux-public/scripts/acceptance-doc-contract-smoke.js
Disasmer release dry run 2bef715211 Public dry run dryrun-20b59dc46b72
Source commit: 20b59dc46b72103c2f8a516c692b5cc3d54fab19

Public tree identity: sha256:aaa5ac7b58b2a0d23c6b11e5f76324bf3839ca1f62653a83deb736932adcfbd9
2026-07-14 10:08:15 +02:00

205 lines
7.6 KiB
JavaScript
Executable file

#!/usr/bin/env node
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const {
assertPreFinalOrFinalLedger,
} = require("./phase3-ledger");
const repo = path.resolve(__dirname, "..");
if (
fs.existsSync(path.join(repo, "DISASMER_PUBLIC_TREE.json")) &&
!fs.existsSync(path.join(repo, "acceptance_criteria.md"))
) {
console.log(
"Acceptance doc contract smoke skipped: root acceptance markdown is filtered from this public tree"
);
process.exit(0);
}
function read(relativePath) {
return fs.readFileSync(path.join(repo, relativePath), "utf8");
}
function criterionLines(source) {
return source
.split(/\r?\n/)
.filter((line) => /^- \[[ x]\] \*\*/.test(line));
}
function assertEveryCriterionHasStatus(source, name) {
const lines = criterionLines(source);
assert(lines.length > 0, `${name} must contain acceptance criteria`);
for (const line of lines) {
assert.match(
line,
/^- \[[ x]\] \*\*(Passed|Partial|Open|Postponed)(?: \([^)]+\))?:\*\*/,
`${name} criterion lacks an explicit status prefix: ${line}`
);
}
}
function assertNoOpenCriteria(source, name) {
const open = criterionLines(source).filter((line) => /\*\*Open(?::| \()/.test(line));
assert.deepStrictEqual(open, [], `${name} still has Open criteria`);
}
function assertPhase3CriterionHeadingsHaveStatus(source, name) {
const headings = source
.split(/\r?\n/)
.filter((line) => /^## /.test(line) && /P3-[A-Z]+-\d{3}:/.test(line));
assert(headings.length > 0, `${name} must contain Phase 3 criteria`);
for (const line of headings) {
assert.match(
line,
/^## \*\*(Passed|Partial|Open|Postponed):\*\* P3-[A-Z]+-\d{3}:/,
`${name} criterion heading lacks an explicit status prefix: ${line}`
);
}
assertPreFinalOrFinalLedger(source);
}
const phase2 = read("acceptance_criteria_phase2.md");
const base = read("acceptance_criteria.md");
const cliFirst = read("cli_acceptance_criteria.md");
const website = read("website_mvp_inventory.md");
const phase3 = read("phase_3_acceptance_criteria.md");
const docsSmoke = read("scripts/docs-smoke.js");
const releaseBlockerSmoke = read("scripts/release-blocker-smoke.js");
const publicAcceptance = read("scripts/acceptance-public.sh");
const privateAcceptance = read("scripts/acceptance-private.sh");
const cliFirstAcceptance = read("scripts/acceptance-cli-first.sh");
const publicSplit = read("scripts/verify-public-split.sh");
assert.match(
phase2,
/phase 2 superset of `acceptance_criteria\.md`/,
"phase 2 criteria must declare that they are a superset of the base criteria"
);
assert.match(
phase2,
/Existing `acceptance_criteria\.md` remains required unless it conflicts with this stricter release document; this document wins in conflicts/,
"phase 2 criteria must keep base acceptance criteria required unless stricter phase 2 criteria conflict"
);
assert.match(
phase2,
/- \[x\] \*\*Passed:\*\* Existing `acceptance_criteria\.md` remains required unless it conflicts with this stricter release document; this document wins in conflicts\./,
"phase 2 cross-document requirement must be marked passed only when this guard is wired"
);
assert.match(
phase3,
/Canonical Phase 3 gate:[\s\S]*active Phase 3 acceptance document/,
"phase 3 criteria must declare itself as the canonical current gate"
);
assert.match(
phase3,
/Important reading note:[\s\S]*not necessarily always mean adding code/,
"phase 3 criteria must keep the no-automatic-code-work disclaimer at the top"
);
assert.match(
phase3,
/Every criterion heading below is prefixed with \*\*Passed\*\*, \*\*Partial\*\*, or \*\*Open\*\*/,
"phase 3 criteria must explain the explicit status-prefix convention"
);
assertPhase3CriterionHeadingsHaveStatus(phase3, "phase_3_acceptance_criteria.md");
for (const [source, name] of [
[base, "acceptance_criteria.md"],
[phase2, "acceptance_criteria_phase2.md"],
[cliFirst, "cli_acceptance_criteria.md"],
]) {
assertEveryCriterionHasStatus(source, name);
assertNoOpenCriteria(source, name);
assert.match(
source,
/Design-document references to billing, paid plans, or plan flags are future metadata placeholders; they do not require MVP/,
`${name} must keep billing/paid-plan placeholders outside MVP implementation scope`
);
assert.match(
source,
/not the canonical Phase 3 release gate; current Phase 3 status and release verification live in `phase_3_acceptance_criteria\.md`/,
`${name} must point current Phase 3 status to phase_3_acceptance_criteria.md`
);
}
assertEveryCriterionHasStatus(website, "website_mvp_inventory.md");
assert.match(
website,
/private hosted website addendum to `acceptance_criteria\.md`, `acceptance_criteria_phase2\.md`, and `cli_acceptance_criteria\.md`/,
"website acceptance criteria must declare itself as the private hosted website addendum"
);
assert.match(
website,
/barebones functional HTML with no CSS/,
"website acceptance criteria must keep the MVP website barebones with no CSS"
);
assert.match(
website,
/Billing is not part of the MVP[\s\S]*Design-document references to billing, paid plans, or plan flags are future metadata placeholders; they do not require MVP website routes/,
"website acceptance criteria must keep billing and paid-plan placeholders outside MVP website scope"
);
assert.match(
website,
/- \[ \] \*\*Open:\*\* The private hosted website acceptance gate exists and exercises the barebones website through `disasmer\.michelpaulissen\.com`/,
"website acceptance criteria must leave the private website deployment gate explicit and open"
);
assert.match(
website,
/not the canonical Phase 3 release gate; current Phase 3 status and release verification live in `phase_3_acceptance_criteria\.md`/,
"website acceptance criteria must point current Phase 3 status to phase_3_acceptance_criteria.md"
);
for (const file of [
"MVP.md",
"acceptance_criteria.md",
"acceptance_criteria_phase2.md",
"cli_acceptance_criteria.md",
"website_mvp_inventory.md",
"phase_3_acceptance_criteria.md",
]) {
assert(
docsSmoke.includes(`"${file}"`),
`docs smoke must include ${file} as user-facing acceptance context`
);
}
assert(
releaseBlockerSmoke.includes('const phase2 = read("acceptance_criteria_phase2.md")'),
"release-blocker smoke must read phase 2 acceptance criteria"
);
assert(
releaseBlockerSmoke.includes('const base = read("acceptance_criteria.md")'),
"release-blocker smoke must read base acceptance criteria"
);
for (const [source, name] of [
[base, "acceptance_criteria.md"],
[phase2, "acceptance_criteria_phase2.md"],
]) {
for (const [label, pattern] of [
["MVP selected locals", /selected (?:top-level )?locals|selected real source locals/],
["MVP task args", /task arguments|task args/],
["MVP handle inspection", /Artifact.*SourceSnapshot.*Blob|Disasmer handles/],
["MVP stdout stderr", /stdout\/stderr/],
["MVP unavailable locals", /cannot be inspected|unavailable-local/],
["MVP required DAP surface", /initialize[\s\S]*launch.*attach[\s\S]*setBreakpoints[\s\S]*configurationDone[\s\S]*threads[\s\S]*stackTrace[\s\S]*scopes[\s\S]*variables[\s\S]*continue[\s\S]*pause/],
]) {
assert.match(source, pattern, `${name} must include MVP debugging criterion: ${label}`);
}
}
for (const [scriptName, script] of [
["public acceptance", publicAcceptance],
["private acceptance", privateAcceptance],
["CLI-first acceptance", cliFirstAcceptance],
["public split", publicSplit],
]) {
assert(
script.includes("node scripts/acceptance-doc-contract-smoke.js"),
`${scriptName} must run acceptance-doc-contract-smoke.js`
);
}
console.log("Acceptance doc contract smoke passed");