import { describe, it, expect } from 'vitest'; import { readFileSync, existsSync } from 'node:fs'; // PIPE-05 doctrine doc-lint test. // // Per RESEARCH § "Validation Architecture" PIPE-05 row, this is the only // automated enforcement of the Phase-1 doctrine documents. CONTEXT D-07 // explicitly forbids a lint rule on UX strings, so this structural test // asserts (a) both docs exist on disk, (b) each contains its required H2 // sections, (c) each cites its required source documents. // // If a future plan moves either doc, update PATH constants below. describe('PIPE-05: doctrine documents exist with required H2 sections', () => { describe('.planning/anti-fomo-doctrine.md', () => { const PATH = '.planning/anti-fomo-doctrine.md'; it('exists', () => { expect(existsSync(PATH)).toBe(true); }); it('contains all 4 required H2 sections', () => { const md = readFileSync(PATH, 'utf8'); expect(md).toMatch(/^## Banned Mechanics$/m); expect(md).toMatch(/^## Allowed Engagement$/m); expect(md).toMatch(/^## Review Checklist$/m); expect(md).toMatch(/^## Source Documents$/m); }); it('cites all 4 source documents (PROJECT, REQUIREMENTS, CLAUDE, PITFALLS)', () => { const md = readFileSync(PATH, 'utf8'); expect(md).toMatch(/PROJECT\.md/); expect(md).toMatch(/REQUIREMENTS\.md/); expect(md).toMatch(/CLAUDE\.md/); expect(md).toMatch(/PITFALLS\.md/); }); it('does NOT propose a lint rule on UX strings (CONTEXT D-07 explicit rejection)', () => { const md = readFileSync(PATH, 'utf8'); // The doc may *mention* that lint rules were rejected, but it must not // propose adding one. Allow "no lint rule" but reject "add a lint rule". expect(md).not.toMatch(/\b(add|implement|propose).{0,40}lint rule/i); }); }); describe('.planning/season-7-end-state.md', () => { const PATH = '.planning/season-7-end-state.md'; it('exists', () => { expect(existsSync(PATH)).toBe(true); }); it('contains all 5 required H2 sections (CONTEXT D-08)', () => { const md = readFileSync(PATH, 'utf8'); expect(md).toMatch(/^## What does \*rest state\* mean\?$/m); expect(md).toMatch(/^## What is the finite Roothold ceiling tied to\?$/m); expect(md).toMatch(/^## What tonal register does the coda live in\?$/m); expect(md).toMatch(/^## What this document is NOT$/m); expect(md).toMatch(/^## Source Documents$/m); }); it('cites SEAS-04, SEAS-09, SEAS-10, STRY-08', () => { const md = readFileSync(PATH, 'utf8'); expect(md).toMatch(/SEAS-04/); expect(md).toMatch(/SEAS-09/); expect(md).toMatch(/SEAS-10/); expect(md).toMatch(/STRY-08/); }); it('does NOT include treatment-level details forbidden by CONTEXT D-08', () => { const md = readFileSync(PATH, 'utf8'); // Check the "What this document is NOT" section is present — this is the // structural guarantee against treatment-level scope creep. expect(md).toMatch(/## What this document is NOT/); // The doc must explicitly disclaim authoring the ending paragraphs. expect(md).toMatch(/authored Phase 7/); }); }); });