#!/usr/bin/env node const assert = require("assert"); const fs = require("fs"); const path = require("path"); const repo = path.resolve(__dirname, ".."); 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`); } const phase2 = read("acceptance_criteria_phase2.md"); const base = read("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 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" ); for (const [source, name] of [ [base, "acceptance_criteria.md"], [phase2, "acceptance_criteria_phase2.md"], ]) { assertEveryCriterionHasStatus(source, name); assertNoOpenCriteria(source, name); } for (const file of ["MVP.md", "acceptance_criteria.md", "acceptance_criteria_phase2.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], ["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");