'use strict';
/**
* Tests for the STATE.md write-path anti-divergence drift guard
* (epic #3408, issue #3468, ADR-3408 Decision 5; SHRUNK per ADR-3473 §8.6,
* issue #3871) — `scripts/lint-state-write-path-drift.cjs`.
*
* Design contract: docs/adr/3408-state-write-path-preservation.md (§8.1/§8.2/§8.3)
* docs/adr/3473-enforcement-by-construction.md (§8.6)
*
* The guard covers five axes, each backed by its own exported pure function
* and each terminal (every finding carries its own `reason` — there is no
* ratchet and nothing here reads a baseline file): `findPolicyDispatchDrift`/
* `findUnimplementedPolicies` (Axis 1, policy dispatch), `findRawStateWrites`
* (Axis 2, a raw `fs.writeFileSync` against the state path),
* `findUnstrippedContentWrites` (Axis 3, a frontmatter-shaped body write),
* `findPromptSeamUses` (Axis 4, prompt-layer prose shelling out to a
* write-side command), and `findCompositionBypasses` (Axis 5, a direct
* `syncStateFrontmatter`/`applyPostSyncPreservation` call outside their
* owner). ADR-3473 §8.6 retired one prior axis's `writeStateMd(` arm and the
* ratchet/retired-baseline machinery that backed it, once `writeStateMd`'s
* third parameter started requiring a `StateTransaction` the type system
* names; issue #3871 review kept that axis's OTHER arm (now
* `findCompositionBypasses`) because the type system gates only
* `writeStateMd`'s parameter, not a call site that never goes through
* `writeStateMd` at all — that arm was made terminal too.
*
* Sections D and E drive each pure function directly with in-memory
* fixtures — no temp tree needed, mirroring
* tests/state-field-drift.test.cjs's own house pattern. Section F instead
* drives the real CLI entry point, via the guard's `--root
` flag
* (`collect(root)` takes a matching parameter, default `REPO_ROOT`, so every
* existing invocation — `npm run lint:ci` included — is unaffected): this is
* what lets a real-tree fixture run inside a disposable `createTempDir()`
* tree instead of ever mutating this repository's own `src/`, where a
* planted fixture would be visible to any concurrent `tsc`/`npm run
* build:lib`/`npm run lint`/another guard invocation, and would survive as
* build-breaking debris if the process were killed before `t.after` ran. D1
* is the one exception among the fixture-driven rows: it asserts the real
* repo's own `src/` and prompt layer are clean, so it runs through the CLI's
* `--json` output against the real `REPO_ROOT` with no `--root` override.
*
* Fixtures use array `.join('\n')`, never an indented template literal —
* indentation bleed would shift every asserted line number.
*
* Assertions compare the frozen `REASON` enum values and the `--json`/pure
* function return shapes only — never a substring/regex match on the human
* formatter's prose (CONTRIBUTING.md, "Prohibited: Raw Text Matching on Test
* Outputs").
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { runNode } = require('./helpers/process-seam.cjs');
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
const { createTempDir, cleanup } = require('./helpers.cjs');
const guard = require('../scripts/lint-state-write-path-drift.cjs');
const {
REASON,
findPromptSeamUses,
findPolicyDispatchDrift,
findUnstrippedContentWrites,
findRawStateWrites,
findCompositionBypasses,
targetsStatePath,
EXECUTOR_FILE,
SEAM_OWNER_FILE,
SEAM_OWNER_EXEMPT_FUNCTIONS,
REPO_ROOT,
} = guard;
const GUARD_PATH = path.join(REPO_ROOT, 'scripts', 'lint-state-write-path-drift.cjs');
// A synthetic, non-executor consumer file — never a real repo path — used
// wherever a row does not specifically need EXECUTOR_FILE behavior.
const OTHER_FILE = 'src/example-consumer.cts';
// ─── D1: the real tree, through the CLI's --json contract ─────────────────
describe('D1 — the real tree', () => {
test('guard: clean tree passes', () => {
const result = runNode([GUARD_PATH, '--json'], { cwd: REPO_ROOT, timeoutMs: PROBE_TIMEOUT_MS });
assert.strictEqual(result.outcome, 'exited');
const body = JSON.parse(result.stdout);
assert.strictEqual(body.ok, true);
assert.deepStrictEqual(body.findings, []);
assert.strictEqual(result.exitCode, 0);
});
});
// ─── D8: comments are not drift (composition-bypass shape) ────────────────
describe('D8 — comments are not drift', () => {
test('guard: comments are not drift', () => {
const text = [
'// syncStateFrontmatter(content, cwd);',
'/**',
' * applyPostSyncPreservation(originalContent, content, synced, statePath, options);',
' */',
'function noop() {}',
].join('\n');
// ADR-3180 Amendment 3's recorded false positive: a `//` line comment
// and a `/* */` block comment both carrying the exact call text must
// stay silent — both are blanked by `stripComments` before the seam-call
// regex ever runs.
assert.deepStrictEqual(findCompositionBypasses(OTHER_FILE, text), []);
});
});
// ─── D9/D10: the owner-file exemption is function-scoped, not file-scoped ──
describe('D9 — owner functions are exempt', () => {
test('guard: owner functions are exempt', () => {
// `syncAndPreserveStateMd` is the sole legitimate place
// `syncStateFrontmatter(` and `applyPostSyncPreservation(` appear
// together (the composition every OTHER caller routes through).
assert.ok(SEAM_OWNER_EXEMPT_FUNCTIONS.includes('syncAndPreserveStateMd'));
const text = [
'function syncAndPreserveStateMd(originalContent, transformedContent, statePath, cwd, options) {',
' const synced = syncStateFrontmatter(transformedContent, cwd, options.authoritativeFm);',
' return applyPostSyncPreservation(originalContent, transformedContent, synced, statePath, options);',
'}',
].join('\n');
// The seam's own internal plumbing (the one owned composition —
// sync then post-sync preservation) is not a bypass.
assert.deepStrictEqual(findCompositionBypasses(SEAM_OWNER_FILE, text), []);
});
test('guard: writeStateMd is also exempt — its own sanctioned direct syncStateFrontmatter call is not a bypass', () => {
assert.ok(SEAM_OWNER_EXEMPT_FUNCTIONS.includes('writeStateMd'));
const text = [
'function writeStateMd(statePath, content, transaction, cwd, clock) {',
" const synced = syncStateFrontmatter(content, cwd, undefined, transaction.kind === 'rebuild');",
' platformWriteSync(statePath, synced);',
'}',
].join('\n');
assert.deepStrictEqual(findCompositionBypasses(SEAM_OWNER_FILE, text), []);
});
});
describe('D10 — the owner file is not exempt', () => {
test('guard: the owner file is not exempt', () => {
// ADR-3408 Decision 5's named gaming route: a whole-FILE exemption on
// the owner is exactly how getMilestoneInfo stayed invisible to an
// earlier drift guard. A call inside any OTHER function in state.cts —
// not one of SEAM_OWNER_EXEMPT_FUNCTIONS — must still be caught.
const text = [
'function patchCore(cwd) {',
' const modified = compute();',
' const synced = syncStateFrontmatter(modified, cwd);',
' return synced;',
'}',
].join('\n');
const out = findCompositionBypasses(SEAM_OWNER_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].reason, REASON.COMPOSITION_BYPASS);
assert.strictEqual(out[0].line, 3);
});
});
// ─── D12: CRLF is scanned identically to LF ────────────────────────────────
describe('D12 — CRLF is scanned identically', () => {
test('guard: CRLF is scanned identically', () => {
const lfText = [
'function cmdSomethingElse(cwd) {',
' const synced = syncStateFrontmatter(modified, cwd);',
'}',
].join('\n');
const crlfText = lfText.split('\n').join('\r\n');
const lfOut = findCompositionBypasses(OTHER_FILE, lfText);
const crlfOut = findCompositionBypasses(OTHER_FILE, crlfText);
assert.strictEqual(crlfOut.length, 1);
const strip = (arr) => arr.map(({ line, symbol, source }) => ({ line, symbol, source }));
assert.deepStrictEqual(strip(crlfOut), strip(lfOut));
// A stray trailing \r surviving into the reported source (the repo's
// documented \n-only-regex bug class) would show up here as a
// sanitized `\x0d` escape — it must not.
assert.strictEqual(crlfOut[0].source, 'const synced = syncStateFrontmatter(modified, cwd);');
});
});
// ─── E1/E2 (Phase 2 / #3469, RETAINED per issue #3871): the composition-pair
// re-assembly shape itself, and the legitimate single-call composition ─────
describe('E1 — a re-assembled composition at a new call site is detected', () => {
test('guard: a call site invoking syncStateFrontmatter and applyPostSyncPreservation directly (bypassing syncAndPreserveStateMd) is caught on BOTH calls', () => {
// Finding 3's exact shape (ADR-3408 Amendment 2): every step calls an
// owner, so neither call alone is undeclared — but assembling the PAIR
// at a call site outside the seam composition is the re-derivation §8.3
// forbids by name. Synthetic: the real instance of this shape
// (cmdPhaseComplete's pre-#3469 adapter) was fixed by that phase.
const text = [
'function cmdReassembledAdapter(cwd, statePath, stateContent) {',
' let synced = syncStateFrontmatter(stateContent, cwd, authoritativeFm);',
' synced = applyPostSyncPreservation(originalStateContent, stateContent, synced, statePath, options);',
' return synced;',
'}',
].join('\n');
const out = findCompositionBypasses(OTHER_FILE, text);
assert.strictEqual(out.length, 2, 'both re-assembled stages must be caught, not just one');
assert.deepStrictEqual(out.map((f) => f.symbol).sort(), ['applyPostSyncPreservation', 'syncStateFrontmatter']);
assert.ok(out.every((f) => f.reason === REASON.COMPOSITION_BYPASS));
});
});
describe('E2 — a legitimate single call to the composition is not detected', () => {
test('guard: calling syncAndPreserveStateMd (the ONE write-seam composition) is not a bypass', () => {
// Verbatim shape from a real caller of the composition (e.g.
// milestone.cts's cmdMilestoneComplete) — a single call to the owned
// composition function, never to its two internal stages directly.
const text = [
' const finalContent = syncAndPreserveStateMd(',
' originalStateContent,',
' result.content,',
' statePath,',
' cwd,',
' {',
' resync: true,',
' authoritativeFm: Object.keys(authoritativeFm).length > 0 ? authoritativeFm : undefined,',
' divergedFields,',
' },',
' );',
].join('\n');
assert.deepStrictEqual(findCompositionBypasses(OTHER_FILE, text), []);
});
});
// ─── D11: the prompt layer is in the scan surface ──────────────────────────
describe('D11 — the prompt layer is in the scan surface', () => {
const PROMPT_FILE = 'gsd-core/workflows/example-workflow.md';
test('guard: the prompt layer is in the scan surface', () => {
const text = [
'# Example workflow',
'',
'Run gsd-tools state.patch --field status --value done to record completion directly.',
].join('\n');
const out = findPromptSeamUses(PROMPT_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].line, 3);
assert.strictEqual(out[0].reason, REASON.PROMPT_LAYER_STATE_WRITE);
assert.strictEqual(out[0].symbol, 'prompt-layer-state-write');
});
test('control: the same candidate wrapped in backticks is a mention, not an invocation, and is not reported', () => {
// Proves the detection above is genuinely exercising the code-span
// exclusion, not merely that the fixture happens to score zero.
const text = 'Documentation only: `gsd-tools state.patch --field status --value done`.';
assert.deepStrictEqual(findPromptSeamUses(PROMPT_FILE, text), []);
});
});
// ─── D14: field-name-keyed BRANCH comparisons (not just CALLS) ────────────
// #3468: `applyPreserveIfPlaceholder` shipped a `field !== 'milestone_name'`
// branch — a field-name-keyed dispatch that routed around
// `getFieldClassification` entirely, so `FIELD_NAME_DISPATCH_RE` (which only
// matches the CALL shape) reported zero violations while the branch shape
// sat in the executor undetected. This section drives the widened detector
// directly against in-memory fixtures, mirroring D2's own "guard: does the
// pure function itself report" pattern.
describe('D14 — field-name-keyed branch comparisons are caught', () => {
test('guard: field !== literal is reported as FIELD_NAME_DISPATCH', () => {
const text = [
'function applyPreserveIfPlaceholder(field, cls, ctx) {',
" if (field !== 'milestone_name') return;",
'}',
].join('\n');
const out = findPolicyDispatchDrift(EXECUTOR_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].reason, REASON.FIELD_NAME_DISPATCH);
assert.strictEqual(out[0].line, 2);
assert.strictEqual(out[0].field, 'milestone_name');
});
test('guard: field === literal is reported as FIELD_NAME_DISPATCH', () => {
const text = [
'function applyPreserveWhenUnchanged(field, cls, ctx) {',
" if (field === 'status') return;",
'}',
].join('\n');
const out = findPolicyDispatchDrift(EXECUTOR_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].reason, REASON.FIELD_NAME_DISPATCH);
assert.strictEqual(out[0].line, 2);
assert.strictEqual(out[0].field, 'status');
});
test('guard: the reversed literal === field is reported as FIELD_NAME_DISPATCH', () => {
const text = [
'function applyPreserveIfPlaceholder(field, cls, ctx) {',
" if ('milestone' === field) return;",
'}',
].join('\n');
const out = findPolicyDispatchDrift(EXECUTOR_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].reason, REASON.FIELD_NAME_DISPATCH);
assert.strictEqual(out[0].line, 2);
assert.strictEqual(out[0].field, 'milestone');
});
test('control: preservation === literal (the CORRECT policy-dispatch shape) is NOT reported', () => {
const text = [
'function applyPreserveAlways(field, cls, ctx) {',
" if (cls.preservation === 'preserve-always') return;",
'}',
].join('\n');
assert.deepStrictEqual(findPolicyDispatchDrift(EXECUTOR_FILE, text), []);
});
test('control: an unrelated variable compared to a literal is NOT reported', () => {
const text = [
'function helper(status, cls, ctx) {',
" if (status !== 'unknown') return;",
'}',
].join('\n');
assert.deepStrictEqual(findPolicyDispatchDrift(EXECUTOR_FILE, text), []);
});
test('control: bracket-indexed field access (ctx.postFm[field]) compared to a non-literal is NOT reported', () => {
const text = [
'function applyPreserveWhenUnchanged(field, cls, ctx) {',
' if (ctx.postFm[field] === snapshot) return;',
'}',
].join('\n');
assert.deepStrictEqual(findPolicyDispatchDrift(EXECUTOR_FILE, text), []);
});
});
// ─── D15: `file` (and other attacker-derived fields) are sanitized AT
// CONSTRUCTION, not just by the human formatter ─────────────────────────────
// Security review finding: a repo can legally track a filename containing C1
// control bytes or bidi-override codepoints — exactly as attacker-controlled
// on a fork PR as the `source` fragment this guard already sanitized before
// this fix. Before this fix `file` reached `--json` stdout unsanitized —
// only the human formatter wrapped it. A finding's `file` (and any other
// attacker-derived field, like `field`) must come back escaped from the
// FINDER itself, so every consumer (human, `--json`) inherits the
// sanitization uniformly.
//
// The two attack codepoints are built via `String.fromCharCode` rather than
// embedded as literal bytes, so this test file's own source never carries a
// live control/bidi codepoint on disk.
describe('D15 — file (and field) values are sanitized at construction', () => {
const RLO = String.fromCharCode(0x202e); // RIGHT-TO-LEFT OVERRIDE (bidi)
const C1_CSI = String.fromCharCode(0x9b); // C1 CONTROL: CSI
const ATTACK_FILE = `src/evil${RLO}${C1_CSI}name.cts`;
const ESCAPED_FILE = 'src/evil\\u202e\\x9bname.cts';
test('findRawStateWrites: an attacker-controlled filename comes back escaped', () => {
const text = ['function cmdSomethingElse(cwd) {', ' fs.writeFileSync(statePath, modified);', '}'].join('\n');
const out = findRawStateWrites(ATTACK_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].file, ESCAPED_FILE);
// Neither raw attack codepoint survives in the finding at all — this is
// exactly what reaches `--json` stdout verbatim (JSON.stringify
// neutralizes C0 but NOT C1 or bidi codepoints, which is why
// construction-time escaping — not JSON.stringify — is load-bearing).
assert.ok(!out[0].file.includes(RLO));
assert.ok(!out[0].file.includes(C1_CSI));
});
test('findPromptSeamUses: an attacker-controlled filename comes back escaped', () => {
const text = 'Run gsd-tools state.patch --field status --value done to record completion directly.';
const out = findPromptSeamUses(ATTACK_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].file, ESCAPED_FILE);
assert.ok(!out[0].file.includes(RLO));
assert.ok(!out[0].file.includes(C1_CSI));
});
test('findPolicyDispatchDrift: filename AND the field literal are both escaped', () => {
const text = [" if (field === 'status" + RLO + C1_CSI + "') return;"].join('\n');
const out = findPolicyDispatchDrift(ATTACK_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].file, ESCAPED_FILE);
assert.strictEqual(out[0].field, 'status\\u202e\\x9b');
assert.ok(!out[0].field.includes(RLO));
assert.ok(!out[0].field.includes(C1_CSI));
});
});
// ─────────────────────────────────────────────────────────────────────────
// Section E (Phase 2 / #3469) — ADR-3408 §8.3 Matrix section E: guard rows
// closing Phase 1's declared known gap (Axis 3, §8.3(b)). E1/E2/E6/E7/E8
// (all of which drove the retired `findSeamBypasses`/ratchet machinery) are
// REMOVED along with it, per ADR-3473 §8.6. E3/E4/E5 (the frontmatter-write
// axis ADR-3473 §8.6 did NOT name for removal) are retained.
// ─────────────────────────────────────────────────────────────────────────
describe('E3 — a patchCore-style frontmatter write is detected (closes the Phase 1 declared gap)', () => {
test('guard: stateReplaceField over unstripped content with a variable field name is caught', () => {
// The pre-Phase-2 shape #3469 fixed: patchCore ran stateReplaceField
// over content that was never stripped of frontmatter, letting a
// lowercase/frontmatter-shaped patch key rewrite the YAML block
// directly, outside FIELD_CLASSIFICATION.
const text = [
'function patchCoreOld(content, intent) {',
' let modified = content;',
' for (const [field, value] of Object.entries(intent.patches)) {',
' const replaced = stateReplaceField(modified, field, value);',
' if (replaced !== null) modified = replaced;',
' }',
' return { content: modified };',
'}',
].join('\n');
const out = findUnstrippedContentWrites(EXECUTOR_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].reason, REASON.UNSTRIPPED_CONTENT_WRITE);
assert.strictEqual(out[0].line, 4);
});
});
describe('E4 — updateCore\'s strip-then-replace is NOT detected', () => {
test('guard: the real updateCore call site (content stripped first) is not flagged', () => {
// Verbatim from src/state-transition.cts's real updateCore — the shape
// Phase 1 measured a naive co-occurrence detector at 29 false positives
// to 1 true positive against; this is one of the 29.
const text = [
'function updateCore(content, intent) {',
' const existingFm = extractFrontmatter(content) as Record;',
' const hasFrontmatter = Object.keys(existingFm).length > 0;',
' const body = stripFrontmatter(content);',
' const result = stateReplaceField(body, intent.field, intent.value);',
' if (result === null) {',
' return { content, updated: [], data: { updated: false } };',
' }',
'}',
].join('\n');
assert.deepStrictEqual(findUnstrippedContentWrites(EXECUTOR_FILE, text), []);
});
});
describe('E5 — sectionBody-scoped stateReplaceField calls are NOT detected', () => {
test('guard: a literal field name against a non-stripFrontmatter-derived section slice is not flagged', () => {
// Verbatim from src/state-transition.cts's real mutateCurrentPositionFirstTime:
// sectionBody is a Current-Position section slice (frontmatter-free by
// construction — it comes from body.slice(...), never from raw content),
// and the field name is a fixed Title-Case literal that can never
// collide with a lowercase/snake_case YAML key. One of the ~20 calls
// Phase 1's naive detector over-reported.
const text = [
'function mutateCurrentPositionFirstTime(body, intent, today, updated) {',
' const span = locateCurrentPosition(body);',
' if (span === null) return body;',
' let sectionBody = body.slice(span.start, span.end);',
' const phaseLabel = `${intent.phaseNumber} — EXECUTING`;',
' if (/^Phase:/m.test(sectionBody)) {',
' sectionBody = sectionBody.replace(/^Phase:.*$/m, `Phase: ${phaseLabel}`);',
' } else {',
" const replaced = stateReplaceField(sectionBody, 'Phase', phaseLabel);",
' if (replaced !== null) sectionBody = replaced;',
' }',
'}',
].join('\n');
assert.deepStrictEqual(findUnstrippedContentWrites(EXECUTOR_FILE, text), []);
});
});
// ─────────────────────────────────────────────────────────────────────────
// F — ADR-3473 §8.6: the raw-write axis (`findRawStateWrites`), and the
// guard's own real CLI entry point proving the shrunk guard can still fail.
// ─────────────────────────────────────────────────────────────────────────
describe('F1 — the raw-write axis: pure function coverage', () => {
test('guard: fs.writeFileSync against statePath is reported', () => {
const text = [
'function bogusRawWrite(statePath, content) {',
' fs.writeFileSync(statePath, content);',
'}',
].join('\n');
const out = findRawStateWrites(OTHER_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].reason, REASON.RAW_STATE_WRITE);
assert.strictEqual(out[0].line, 2);
assert.strictEqual(out[0].source, 'fs.writeFileSync(statePath, content);');
});
test('guard: fs.writeFileSync against a STATE.md literal is reported', () => {
const text = [
'function bogusRawWrite(cwd, content) {',
" fs.writeFileSync(path.join(cwd, 'STATE.md'), content);",
'}',
].join('\n');
const out = findRawStateWrites(OTHER_FILE, text);
assert.strictEqual(out.length, 1);
assert.strictEqual(out[0].reason, REASON.RAW_STATE_WRITE);
});
test('control: fs.writeFileSync against an unrelated target is NOT reported', () => {
const text = ['function writeSomethingElse(otherPath, content) {', ' fs.writeFileSync(otherPath, content);', '}'].join(
'\n',
);
assert.deepStrictEqual(findRawStateWrites(OTHER_FILE, text), []);
});
test('control: platformWriteSync (the sanctioned seam) against statePath is NOT reported — a different call, by name', () => {
// The type/seam this axis exists BESIDE, not instead of: every real
// STATE.md writer in this codebase calls `platformWriteSync`, never raw
// `fs.writeFileSync`, against `statePath`. This axis only matches the
// literal `fs.writeFileSync` call shape.
const text = ['function realWriter(statePath, content) {', ' platformWriteSync(statePath, content);', '}'].join(
'\n',
);
assert.deepStrictEqual(findRawStateWrites(OTHER_FILE, text), []);
});
test('guard: comments are not drift', () => {
const text = [
'// fs.writeFileSync(statePath, modified);',
'/**',
' * fs.writeFileSync(statePath, modified);',
' */',
'function noop() {}',
].join('\n');
assert.deepStrictEqual(findRawStateWrites(OTHER_FILE, text), []);
});
test('targetsStatePath: bare identifier and STATE.md-literal both match; an unrelated identifier does not', () => {
assert.ok(targetsStatePath('statePath'));
assert.ok(targetsStatePath("path.join(cwd, 'STATE.md')"));
assert.ok(!targetsStatePath('otherPath'));
});
});
describe('F2 — a guard that cannot fail is not a guard: the real CLI entry point catches a raw write', () => {
test('CLI: --root with fs.writeFileSync(statePath, ...) is reported, without touching the real src/ tree', (t) => {
// A throwaway tree in an OS temp dir, never inside this repository — the
// guard's own `--root` flag (default REPO_ROOT, so every OTHER caller of
// this CLI is unaffected) is what makes this possible without mutating
// the repo under test. See this file's header for why a real-src/
// fixture was rejected.
const tmpRoot = createTempDir('state-write-path-drift-guard-');
t.after(() => cleanup(tmpRoot));
const srcDir = path.join(tmpRoot, 'src');
fs.mkdirSync(srcDir, { recursive: true });
const fixtureContent = [
"import * as fs from 'node:fs';",
'',
'function bogusRawWrite(statePath: string, content: string): void {',
' fs.writeFileSync(statePath, content);',
'}',
].join('\n');
fs.writeFileSync(path.join(srcDir, 'bogus.cts'), fixtureContent, 'utf8');
const result = runNode([GUARD_PATH, '--root', tmpRoot, '--json'], { cwd: REPO_ROOT, timeoutMs: PROBE_TIMEOUT_MS });
assert.strictEqual(result.outcome, 'exited');
assert.strictEqual(result.exitCode, 1, 'a real raw write against statePath must fail the CLI, not pass it');
const body = JSON.parse(result.stdout);
assert.strictEqual(body.ok, false);
const finding = body.findings.find((f) => f.file === 'src/bogus.cts');
assert.ok(finding, 'the planted fixture must appear in --json findings');
assert.strictEqual(finding.reason, REASON.RAW_STATE_WRITE);
assert.strictEqual(finding.line, 4);
});
test('CLI: --root passes, proving --root does not silently widen scope back to REPO_ROOT', (t) => {
const tmpRoot = createTempDir('state-write-path-drift-guard-clean-');
t.after(() => cleanup(tmpRoot));
const srcDir = path.join(tmpRoot, 'src');
fs.mkdirSync(srcDir, { recursive: true });
fs.writeFileSync(path.join(srcDir, 'clean.cts'), "export const noop = () => 'noop';\n", 'utf8');
const result = runNode([GUARD_PATH, '--root', tmpRoot, '--json'], { cwd: REPO_ROOT, timeoutMs: PROBE_TIMEOUT_MS });
assert.strictEqual(result.outcome, 'exited');
assert.strictEqual(result.exitCode, 0);
const body = JSON.parse(result.stdout);
assert.strictEqual(body.ok, true);
assert.deepStrictEqual(body.findings, []);
});
});
describe('F3 — a guard that cannot fail is not a guard: the real CLI entry point catches a composition bypass', () => {
test('CLI: --root with a re-assembled syncStateFrontmatter + applyPostSyncPreservation pair is reported, without touching the real src/ tree', (t) => {
const tmpRoot = createTempDir('state-write-path-drift-guard-composition-');
t.after(() => cleanup(tmpRoot));
const srcDir = path.join(tmpRoot, 'src');
fs.mkdirSync(srcDir, { recursive: true });
const fixtureContent = [
'function cmdReassembledAdapter(cwd: string, statePath: string, stateContent: string): string {',
' let synced = syncStateFrontmatter(stateContent, cwd, authoritativeFm);',
' synced = applyPostSyncPreservation(originalStateContent, stateContent, synced, statePath, options);',
' return synced;',
'}',
].join('\n');
fs.writeFileSync(path.join(srcDir, 'bogus-composition.cts'), fixtureContent, 'utf8');
const result = runNode([GUARD_PATH, '--root', tmpRoot, '--json'], { cwd: REPO_ROOT, timeoutMs: PROBE_TIMEOUT_MS });
assert.strictEqual(result.outcome, 'exited');
assert.strictEqual(result.exitCode, 1, 'a re-assembled write-seam composition must fail the CLI, not pass it');
const body = JSON.parse(result.stdout);
assert.strictEqual(body.ok, false);
const findings = body.findings.filter((f) => f.file === 'src/bogus-composition.cts');
assert.strictEqual(findings.length, 2, 'both re-assembled stages must appear in --json findings');
assert.ok(findings.every((f) => f.reason === REASON.COMPOSITION_BYPASS));
});
});