diff --git a/scripts/free-test-durations.json b/scripts/free-test-durations.json index 197568979..4e55d23a3 100644 --- a/scripts/free-test-durations.json +++ b/scripts/free-test-durations.json @@ -867,7 +867,6 @@ "test/spawnsync-timeout-tripwire.test.ts": 163, "test/spec-quality-gate-secret-sink.test.ts": 1048, "test/spec-template-invariants.test.ts": 23, - "test/spec-template-sync.test.ts": 145, "test/static-no-legacy-writes.test.ts": 1195, "test/strict-output-capture.test.ts": 89, "test/strict-output-formats.test.ts": 899, diff --git a/test/spec-template-sync.test.ts b/test/spec-template-sync.test.ts deleted file mode 100644 index 2cdc7224d..000000000 --- a/test/spec-template-sync.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * spec-template-sync: verify /spec templates ↔ generated docs stay in sync. - * - * Per codex T8 / eng plan: regen and assert no drift. Catches commits that - * edit a template but forget to run `bun run gen:skill-docs`, or vice versa. - * - * /spec is carved (skeleton + sections/gate-and-file.md), so BOTH generated - * artifacts are checked: a stale section is the same drift bug as a stale - * skeleton — the on-demand file is what the agent executes at Phase 4.5. - * - * The regen renders into an isolated --out-dir and compares the rendered - * bytes against the TRACKED files — the working tree is only ever read. - */ -import { describe, test, expect } from 'bun:test'; -import * as fs from 'fs'; -import * as os from 'os'; -import * as path from 'path'; -import { spawnSync } from 'child_process'; - -const ROOT = path.resolve(import.meta.dir, '..'); - -const GENERATED_PATHS = [ - path.join(ROOT, 'spec', 'SKILL.md'), - path.join(ROOT, 'spec', 'sections', 'gate-and-file.md'), -]; - -describe('/spec template/generated sync', () => { - test('regenerating spec/SKILL.md + sections produces byte-identical output', () => { - const outDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-spec-sync-')); - - const res = spawnSync('bun', ['run', 'gen:skill-docs', '--out-dir', outDir], { - cwd: ROOT, - encoding: 'utf-8', - timeout: 120_000, - // Scrubbed env: bun test runs a shard's files serially in ONE process, - // so an earlier test's env mutations (GSTACK_*/GBRAIN_* detection vars) - // leak into inherited process.env and change generator output — this - // test failed in-suite while passing solo on an identical tree. The - // generator's output must be a function of the templates, not of - // whichever test ran before this one. - env: { - PATH: process.env.PATH ?? '', - HOME: process.env.HOME ?? '', - TMPDIR: process.env.TMPDIR ?? '', - }, - }); - try { - expect(res.status).toBe(0); - - for (const trackedPath of GENERATED_PATHS) { - const rel = path.relative(ROOT, trackedPath); - const rendered = fs.readFileSync(path.join(outDir, rel), 'utf-8'); - // --out-dir repoints the literal section-base paths - // (~/.claude/skills/gstack//sections/ → //sections/) - // so section Reads resolve inside the render. Undo that single - // documented rewrite before comparing; every OTHER byte must match - // the tracked file exactly. - const normalized = rendered.replaceAll(`${outDir}/`, '~/.claude/skills/gstack/'); - const tracked = fs.readFileSync(trackedPath, 'utf-8'); - expect({ file: rel, identical: normalized === tracked }) - .toEqual({ file: rel, identical: true }); - } - } finally { - fs.rmSync(outDir, { recursive: true, force: true }); - } - }, 130_000); - - test('generated /spec docs carry the auto-generated header', () => { - for (const p of GENERATED_PATHS) { - const generated = fs.readFileSync(p, 'utf-8'); - expect(generated).toMatch(/AUTO-GENERATED|do not edit directly/i); - } - }); -});