61 lines
2.7 KiB
JavaScript
61 lines
2.7 KiB
JavaScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import process from 'node:process';
|
|
|
|
const root = process.cwd();
|
|
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
|
|
const failures = [];
|
|
|
|
const scope = read('MVP_SCOPE.md');
|
|
const requirements = read('docs/REQUIREMENTS.md');
|
|
const readme = read('README.md');
|
|
const roadmap = read('ROADMAP.md');
|
|
const traceability = read('docs/MVP_TRACEABILITY.md');
|
|
|
|
const requireText = (file, text, label) => {
|
|
if (!read(file).includes(text)) failures.push(`${file}: missing ${label}`);
|
|
};
|
|
|
|
requireText('MVP_SCOPE.md', 'The initial MVP has no AI dependency.', 'approved no-AI boundary');
|
|
requireText('docs/REQUIREMENTS.md', '`MVP_SCOPE.md` is the approved v0.1.0 release boundary.', 'scope authority');
|
|
requireText('docs/REQUIREMENTS.md', '## 12. Resolved MVP Decisions', 'resolved-decision section');
|
|
requireText('docs/MVP_TRACEABILITY.md', '## Release-critical requirements', 'requirements matrix');
|
|
requireText('docs/MVP_TRACEABILITY.md', '## Explicit post-MVP classifications', 'post-MVP classification');
|
|
|
|
for (const [file, text, pattern] of [
|
|
['README.md', readme, /AI Assistance.*IBM Bob|watsonx can generate/i],
|
|
['docs/REQUIREMENTS.md', requirements, /IBM Bob \/ watsonx should assist|Use IBM Bob\/watsonx to generate/i],
|
|
['docs/REQUIREMENTS.md', requirements, /^## 12\. Open Questions$/m],
|
|
['docs/REQUIREMENTS.md', requirements, /^\* Timeout setting$/m],
|
|
['docs/REQUIREMENTS.md', requirements, /^\* Expected response format$/m],
|
|
]) {
|
|
if (pattern.test(text)) failures.push(`${file}: forbidden stale MVP claim matches ${pattern}`);
|
|
}
|
|
|
|
if (/^- \[ \]/m.test(scope)) failures.push('MVP_SCOPE.md: an accepted workflow checkbox is unchecked');
|
|
if (!roadmap.includes('| [Slice 7](SLICE7.md) | MVP scope decision and requirement governance | Complete | None |')) {
|
|
failures.push('ROADMAP.md: Slice 7 is not recorded as complete with no remaining gate');
|
|
}
|
|
|
|
const linkedFiles = ['README.md', 'MVP_SCOPE.md', 'ROADMAP.md', 'SLICE7.md', 'docs/REQUIREMENTS.md', 'docs/ARCHITECTURE.md', 'docs/MVP_TRACEABILITY.md'];
|
|
for (const file of linkedFiles) {
|
|
for (const match of read(file).matchAll(/\[[^\]]+\]\(([^)]+)\)/g)) {
|
|
const target = match[1].split('#')[0];
|
|
if (!target || /^[a-z]+:/i.test(target)) continue;
|
|
if (!fs.existsSync(path.resolve(root, path.dirname(file), target))) {
|
|
failures.push(`${file}: unresolved relative link ${target}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let id = 1; id <= 18; id += 1) {
|
|
if (!traceability.includes(`| R${id} |`)) failures.push(`docs/MVP_TRACEABILITY.md: missing R${id}`);
|
|
}
|
|
|
|
if (failures.length > 0) {
|
|
console.error(failures.join('\n'));
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log('MVP governance check passed: scope, R1-R18 traceability, classifications, and links are consistent.');
|