voyage/tests/validators/intent-approval.test.mjs
Kjell Tore Guttormsen 097bae8a02
test(intent): M3–M7 — halt pinned where it acts, allowlist guarded, header claims pinned
M4: the halt regex matched any **halt** in a 2500-char window, so the PM's
mutant (first **halt** → "continue anyway") survived. Two new tests pin the
sentence right after the gate command and the could-not-run halt. Mutant
run: old test ok, both new tests not ok.
M5: nothing guarded 2045432's allowlist line. New test: trekbrief keeps
intent_approved, drops task/project_dir. Mutant (line removed): not ok.
M3 (red): the header claims a reflow keeps the approval; joining lines is
STALE. Pins the honest wording + the STALE behaviour.
M7 (red): the trekreview exemption is not documented as a bypass of the
same trust class as self-stamping (header, HANDOVER-CONTRACTS, trekplan).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 09:23:04 +02:00

486 lines
23 KiB
JavaScript

// tests/validators/intent-approval.test.mjs
// Veikart steg 1 — «Intensjonen først»: /trekplan stops without an approved intent.
//
// The four order cases, each bound to the COMMAND PROSE rather than to a module
// name alone: the gate line is extracted from commands/trekplan.md and the stamp
// line from commands/trekbrief.md, and those exact lines are executed. Remove the
// line from the prose, or stub the module, and these tests fall.
// (a) brief without a marker → /trekplan's check exits non-zero
// (b) marker + intent text edited after → exits non-zero (stale)
// (c) valid marker → exits 0 (known negative)
// (d) the stamp runs before the manual/auto fork and emits brief-approved
//
// The module is imported dynamically so a missing module fails each test on its
// own instead of crashing the file into a single failure.
import { test } from 'node:test';
import { strict as assert } from 'node:assert';
import { spawnSync } from 'node:child_process';
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, existsSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const HERE = dirname(fileURLToPath(import.meta.url));
const ROOT = join(HERE, '..', '..');
const MODULE = join(ROOT, 'lib', 'validators', 'intent-approval.mjs');
const load = () => import(MODULE);
const read = (rel) => readFileSync(join(ROOT, rel), 'utf8');
// A generic brief — no real organisation, no real corpus.
const BRIEF = `---
type: trekbrief
brief_version: "2.2"
created: 2026-09-23
task: "Add a dark-mode toggle to a recipe app"
slug: dark-mode-toggle
project_dir: .claude/projects/2026-09-23-dark-mode-toggle/
research_topics: 0
research_status: complete
auto_research: false
interview_turns: 4
source: interview
framing: new-direction
phase_signals_partial: true
---
# Task: Dark-mode toggle
## TL;DR
Readers cook at night; the white page glares.
## Intent
Readers cook in dim kitchens and the bright page glares.
They want the app to follow the system theme.
## Goal
A toggle in settings, defaulting to the system theme,
remembered across restarts.
## Success Criteria
- Toggle persists across restarts.
`;
function tmpDir() { return mkdtempSync(join(tmpdir(), 'intent-approval-')); }
/** The single bash line in `rel` that invokes intent-approval.mjs with `verb`. */
function commandLine(rel, verb) {
const lines = read(rel).split('\n').filter((l) =>
/^\s*node \$\{CLAUDE_PLUGIN_ROOT\}\/lib\/validators\/intent-approval\.mjs\b/.test(l) && l.includes(verb));
assert.equal(lines.length, 1, `${rel} must carry exactly one \`intent-approval.mjs ${verb}\` line; found ${lines.length}`);
return lines[0].trim();
}
function run(line, subs, env = {}) {
let cmd = line.split('${CLAUDE_PLUGIN_ROOT}').join(ROOT);
for (const [k, v] of Object.entries(subs)) cmd = cmd.split(k).join(v);
return spawnSync('bash', ['-c', cmd], { encoding: 'utf8', env: { ...process.env, ...env } });
}
function gate(briefPath) {
return run(commandLine('commands/trekplan.md', '--check'), { '{brief_path}': briefPath });
}
function stampInto(dir, env) {
// {BRIEF_PATH} since M2 (the resolved --approve target); {PROJECT_DIR} kept for the old spelling
return run(commandLine('commands/trekbrief.md', '--stamp'),
{ '{BRIEF_PATH}': join(dir, 'brief.md'), '{PROJECT_DIR}': dir }, env);
}
function codes(res) {
const out = JSON.parse(res.stdout);
return out.errors.map((e) => e.code);
}
// ---- (a) (b) (c): the /trekplan gate, executed as the command prose spells it ----
test('(a) /trekplan gate: brief WITHOUT an approval marker stops (exit != 0, BRIEF_INTENT_NOT_APPROVED)', () => {
const dir = tmpDir();
try {
const p = join(dir, 'brief.md');
writeFileSync(p, BRIEF);
const r = gate(p);
assert.notEqual(r.status, 0, `gate must stop on an unapproved brief; stdout=${r.stdout} stderr=${r.stderr}`);
assert.ok(codes(r).includes('BRIEF_INTENT_NOT_APPROVED'), r.stdout);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('(b) /trekplan gate: marker present but ## Intent edited after approval stops (BRIEF_INTENT_APPROVAL_STALE)', async () => {
const { stampIntentApproval } = await load();
const dir = tmpDir();
try {
const p = join(dir, 'brief.md');
const stamped = stampIntentApproval(BRIEF, new Date('2026-09-23T08:00:00Z')).text;
writeFileSync(p, stamped.replace('follow the system theme', 'always be dark'));
const r = gate(p);
assert.notEqual(r.status, 0, `gate must stop on a stale approval; stdout=${r.stdout}`);
assert.ok(codes(r).includes('BRIEF_INTENT_APPROVAL_STALE'), r.stdout);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('(b) /trekplan gate: editing ## Goal after approval also stops', async () => {
const { stampIntentApproval } = await load();
const dir = tmpDir();
try {
const p = join(dir, 'brief.md');
const stamped = stampIntentApproval(BRIEF, new Date('2026-09-23T08:00:00Z')).text;
writeFileSync(p, stamped.replace('remembered across restarts', 'reset on every launch'));
const r = gate(p);
assert.notEqual(r.status, 0);
assert.ok(codes(r).includes('BRIEF_INTENT_APPROVAL_STALE'), r.stdout);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('(c) /trekplan gate: a valid marker passes (exit 0) — known negative', async () => {
const { stampIntentApproval } = await load();
const dir = tmpDir();
try {
const p = join(dir, 'brief.md');
writeFileSync(p, stampIntentApproval(BRIEF, new Date('2026-09-23T08:00:00Z')).text);
const r = gate(p);
assert.equal(r.status, 0, `valid approval must pass; stdout=${r.stdout} stderr=${r.stderr}`);
assert.deepEqual(codes(r), []);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('(c) editing text OUTSIDE ## Intent / ## Goal keeps the approval valid', async () => {
const { stampIntentApproval, checkIntentApprovalContent } = await load();
const stamped = stampIntentApproval(BRIEF, new Date('2026-09-23T08:00:00Z')).text;
const edited = stamped.replace('- Toggle persists across restarts.', '- Toggle persists across restarts.\n- Contrast passes AA.');
assert.deepEqual(checkIntentApprovalContent(edited).errors, []);
});
test('whitespace-only reflow of ## Intent keeps the approval valid (normalized hash)', async () => {
const { stampIntentApproval, checkIntentApprovalContent } = await load();
const stamped = stampIntentApproval(BRIEF, new Date('2026-09-23T08:00:00Z')).text;
const reflowed = stamped.replace(
'Readers cook in dim kitchens and the bright page glares.\nThey want',
'Readers cook in dim kitchens and the bright page glares.\n\n They want');
assert.notEqual(reflowed, stamped);
assert.deepEqual(checkIntentApprovalContent(reflowed).errors, []);
});
test('a malformed marker is BRIEF_INTENT_APPROVAL_INVALID, not a pass', async () => {
const { checkIntentApprovalContent } = await load();
const bad = BRIEF.replace('source: interview\n', 'source: interview\nintent_approved_hash: "yes"\n');
const codesOut = checkIntentApprovalContent(bad).errors.map((e) => e.code);
assert.deepEqual(codesOut, ['BRIEF_INTENT_APPROVAL_INVALID']);
});
test('a brief missing ## Goal cannot be approved: stamp refuses, check is INVALID', async () => {
const { stampIntentApproval, checkIntentApprovalContent } = await load();
const noGoal = BRIEF.replace(/## Goal\n\n[\s\S]*?\n\n## Success/, '## Success');
assert.equal(stampIntentApproval(noGoal).stamped, false);
const forged = noGoal.replace('source: interview\n',
`source: interview\nintent_approved_hash: "sha256:${'0'.repeat(64)}"\n`);
assert.deepEqual(checkIntentApprovalContent(forged).errors.map((e) => e.code), ['BRIEF_INTENT_APPROVAL_INVALID']);
});
test('trekreview briefs are exempt from the gate (they carry no ## Intent of their own)', async () => {
const { checkIntentApprovalContent } = await load();
const review = `---
type: trekreview
task: "Review delivered toggle"
slug: dark-mode-toggle
project_dir: .claude/projects/2026-09-23-dark-mode-toggle/
findings:
- 0123456789abcdef0123456789abcdef01234567
---
# Review brief
`;
assert.deepEqual(checkIntentApprovalContent(review).errors, []);
});
test('the gate message tells the operator what to do (/trekbrief --approve)', () => {
const dir = tmpDir();
try {
const p = join(dir, 'brief.md');
writeFileSync(p, BRIEF);
const out = JSON.parse(gate(p).stdout);
const e = out.errors.find((x) => x.code === 'BRIEF_INTENT_NOT_APPROVED');
assert.ok(e && /\/trekbrief --approve/.test(`${e.message} ${e.hint || ''}`), JSON.stringify(e));
} finally { rmSync(dir, { recursive: true, force: true }); }
});
// ---- /trekplan prose: the gate halts, on both brief modes, before any planning ----
test('trekplan prose: the intent-approval gate sits in "Read the brief" and says HALT', () => {
const text = read('commands/trekplan.md');
const readIdx = text.indexOf('### Read the brief');
const gateIdx = text.indexOf(commandLine('commands/trekplan.md', '--check'));
const phase2 = text.indexOf('\n## Phase 2');
assert.ok(readIdx >= 0 && gateIdx > readIdx, 'gate must sit under ### Read the brief (runs for --brief AND --project)');
assert.ok(phase2 < 0 || gateIdx < phase2, 'gate must run before Phase 2');
const section = text.slice(gateIdx, gateIdx + 2500);
assert.match(section, /BRIEF_INTENT_NOT_APPROVED/);
assert.match(section, /BRIEF_INTENT_APPROVAL_STALE/);
assert.match(section, /\*\*halt\*\*/i);
});
// ---- (d) the stamp: operator answer → marker + brief-approved, on BOTH paths ----
test('(d) trekbrief: the stamp step runs BEFORE the Phase 5 manual/auto fork', () => {
const text = read('commands/trekbrief.md');
const stampIdx = text.indexOf(commandLine('commands/trekbrief.md', '--stamp'));
const phase5 = text.indexOf('## Phase 5');
const manual = text.indexOf('### Manual path');
assert.ok(phase5 > 0 && manual > 0, 'Phase 5 / Manual path headings must exist');
assert.ok(stampIdx > 0 && stampIdx < phase5 && stampIdx < manual,
'the stamp must run before the fork, so the manual path passes through it');
// The approval is the operator's explicit answer, asked even in --quick.
const before = text.slice(text.lastIndexOf('\n## Phase 4h', stampIdx), stampIdx);
assert.match(before, /AskUserQuestion/);
assert.match(before, /--quick/);
});
test('(d) trekbrief: the auto path no longer emits brief-approved on its own (one emission, not two)', () => {
const text = read('commands/trekbrief.md');
const auto = text.indexOf('### Auto path');
const phase6 = text.indexOf('## Phase 6');
assert.ok(auto > 0 && phase6 > auto);
assert.ok(!/--event brief-approved/.test(text.slice(auto, phase6)),
'brief-approved is emitted by the stamp; a second emission on the auto path would double-count');
});
test('(d) the manual-path stamp line writes the marker AND emits a brief-approved record', () => {
const dir = tmpDir();
const data = tmpDir();
try {
writeFileSync(join(dir, 'brief.md'), BRIEF);
const r = stampInto(dir, { CLAUDE_PLUGIN_DATA: data });
assert.equal(r.status, 0, `stamp failed: ${r.stdout} ${r.stderr}`);
const after = readFileSync(join(dir, 'brief.md'), 'utf8');
assert.match(after, /^intent_approved_hash: "sha256:[0-9a-f]{64}"$/m);
assert.match(after, /^intent_approved_at: "\d{4}-\d\d-\d\dT[^"]+"$/m);
const statsFile = join(data, 'trekexecute-stats.jsonl');
assert.ok(existsSync(statsFile), 'brief-approved record must be written');
const recs = readFileSync(statsFile, 'utf8').trim().split('\n').map((l) => JSON.parse(l));
const ev = recs.filter((x) => x.event === 'brief-approved');
assert.equal(ev.length, 1);
assert.equal(ev[0].payload.slug, 'dark-mode-toggle');
assert.match(ev[0].payload.intent_hash, /^sha256:[0-9a-f]{64}$/);
// and the gate now passes on the stamped file
assert.equal(gate(join(dir, 'brief.md')).status, 0);
} finally {
rmSync(dir, { recursive: true, force: true });
rmSync(data, { recursive: true, force: true });
}
});
test('(d) with CLAUDE_PLUGIN_DATA unset the record still lands (fallback = the dir the yardstick reads)', async () => {
const { resolveApprovalDataDir } = await load();
const d = resolveApprovalDataDir({ HOME: '/home/someone' });
assert.equal(d, '/home/someone/.claude/plugins/data/voyage-ktg-plugin-marketplace');
assert.equal(resolveApprovalDataDir({ CLAUDE_PLUGIN_DATA: '/x/y', HOME: '/h' }), '/x/y');
});
test('re-stamping replaces the marker instead of duplicating it', async () => {
const { stampIntentApproval } = await load();
const once = stampIntentApproval(BRIEF, new Date('2026-09-23T08:00:00Z')).text;
const twice = stampIntentApproval(once, new Date('2026-09-24T08:00:00Z')).text;
assert.equal((twice.match(/^intent_approved_hash:/gm) || []).length, 1);
assert.equal((twice.match(/^intent_approved_at:/gm) || []).length, 1);
assert.match(twice, /2026-09-24T08:00:00/);
});
// ---- honesty: the code says what the marker does NOT prove ----
test('the module states what the marker does NOT prove (trace, not a signature)', () => {
assert.ok(existsSync(MODULE), 'intent-approval.mjs missing');
const src = readFileSync(MODULE, 'utf8');
assert.match(src, /NOT a signature/);
assert.match(src, /same user, same machine/i);
});
// ---- /trekbrief --approve: the stop message's remedy exists ----
test('/trekbrief --approve is a parsed flag and a documented mode', async () => {
const { parseArgs } = await import(join(ROOT, 'lib', 'parsers', 'arg-parser.mjs'));
const r = parseArgs('--approve .claude/projects/2026-09-23-dark-mode-toggle', 'trekbrief');
assert.equal(r.flags['--approve'], '.claude/projects/2026-09-23-dark-mode-toggle');
assert.deepEqual(r.unknown, []);
assert.deepEqual(r.errors, []);
assert.match(read('commands/trekbrief.md'), /--approve <project-dir \| brief-file>/);
assert.match(read('docs/command-modes.md'), /`--approve <project-dir \\\| brief-file>`/);
});
// ---- M1: the parser fails CLOSED on extra, hidden or cut-off sections ----
// (PM re-measurement 2026-09-23, probes vi-a/vi-b/vii-a/vii-b/vii-c: all passed the gate)
const STAMP_AT = new Date('2026-09-23T08:00:00Z');
const FENCE = '```';
test('M1 (vi-a): a second ## Intent added AFTER approval does not pass the gate', async () => {
const { stampIntentApproval, checkIntentApprovalContent } = await load();
const stamped = stampIntentApproval(BRIEF, STAMP_AT).text;
const extra = stamped.replace('## Success Criteria', '## Intent\n\nActually: make it always dark.\n\n## Success Criteria');
const r = checkIntentApprovalContent(extra);
assert.equal(r.valid, false, 'an added ## Intent must not ride on the old approval');
assert.deepEqual(r.errors.map((e) => e.code), ['BRIEF_INTENT_APPROVAL_INVALID']);
});
test('M1 (vi-b): a brief with two ## Intent (or two ## Goal) sections cannot be stamped', async () => {
const { stampIntentApproval } = await load();
const twoIntents = BRIEF.replace('## Goal', '## Intent\n\nA second intent.\n\n## Goal');
assert.equal(stampIntentApproval(twoIntents).stamped, false);
const twoGoals = BRIEF.replace('## Success Criteria', '## Goal\n\nA second goal.\n\n## Success Criteria');
assert.equal(stampIntentApproval(twoGoals).stamped, false);
});
test('M1 (vii-a): a ## Intent inside a code block before the real one does not capture the hash', async () => {
const { stampIntentApproval, checkIntentApprovalContent } = await load();
const withExample = BRIEF.replace('## TL;DR', `## TL;DR\n\n${FENCE}markdown\n## Intent\n\nExample text.\n${FENCE}`);
const stamped = stampIntentApproval(withExample, STAMP_AT).text;
const edited = stamped.replace('follow the system theme', 'always be dark');
assert.deepEqual(checkIntentApprovalContent(edited).errors.map((e) => e.code), ['BRIEF_INTENT_APPROVAL_STALE']);
});
test('M1 (vii-b): a ## Intent inside an HTML comment before the real one does not capture the hash', async () => {
const { stampIntentApproval, checkIntentApprovalContent } = await load();
const withComment = BRIEF.replace('## TL;DR', '## TL;DR\n\n<!--\n## Intent\n\nOld draft.\n-->');
const stamped = stampIntentApproval(withComment, STAMP_AT).text;
const edited = stamped.replace('follow the system theme', 'always be dark');
assert.deepEqual(checkIntentApprovalContent(edited).errors.map((e) => e.code), ['BRIEF_INTENT_APPROVAL_STALE']);
});
test('M1 (vii-c): a "## " line inside a code block in ## Intent does not cut the section short', async () => {
const { stampIntentApproval, checkIntentApprovalContent } = await load();
const withBlock = BRIEF.replace('They want the app to follow the system theme.',
`${FENCE}\n## not a heading\n${FENCE}\nThey want the app to follow the system theme.`);
const stamped = stampIntentApproval(withBlock, STAMP_AT).text;
const edited = stamped.replace('follow the system theme', 'always be dark');
assert.deepEqual(checkIntentApprovalContent(edited).errors.map((e) => e.code), ['BRIEF_INTENT_APPROVAL_STALE']);
});
test('M1 (vii-d, kept): an HTML comment INSIDE ## Intent is part of the approved text', async () => {
const { stampIntentApproval, checkIntentApprovalContent } = await load();
const withNote = BRIEF.replace('They want', '<!-- note: v1 -->\nThey want');
const stamped = stampIntentApproval(withNote, STAMP_AT).text;
const edited = stamped.replace('note: v1', 'note: v2');
assert.deepEqual(checkIntentApprovalContent(edited).errors.map((e) => e.code), ['BRIEF_INTENT_APPROVAL_STALE']);
});
test('M1: a plain brief hashes the same as before the parser change (no stamped brief goes stale)', async () => {
const { computeIntentHash } = await load();
// pinned from 66e1fa1's computeIntentHash(BRIEF)
assert.equal(computeIntentHash(BRIEF.split('---\n').slice(2).join('---\n')).hash,
'sha256:e722491047867492fde099d6048c46ee673229b00bebe90f0484a1943a199da9');
});
// ---- M2: --approve reaches a brief that is not <dir>/brief.md ----
// (/trekplan --brief docs/x-brief.md had no approval path but a raw --stamp)
function resolveApprove(arg) {
const line = commandLine('commands/trekbrief.md', '--resolve');
return run(line, { '{APPROVE_ARG}': arg });
}
test('M2: /trekbrief --approve <brief-file> resolves to that file and names /trekplan --brief', () => {
const dir = tmpDir();
try {
mkdirSync(join(dir, 'docs'));
const p = join(dir, 'docs', 'x-brief.md');
writeFileSync(p, BRIEF);
const r = resolveApprove(p);
assert.equal(r.status, 0, r.stdout + r.stderr);
const out = JSON.parse(r.stdout);
assert.equal(out.brief_path, p);
assert.equal(out.plan_command, `/trekplan --brief ${p}`);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('M2: /trekbrief --approve <project-dir> still resolves to <dir>/brief.md and /trekplan --project', () => {
const dir = tmpDir();
try {
writeFileSync(join(dir, 'brief.md'), BRIEF);
const r = resolveApprove(`${dir}/`);
assert.equal(r.status, 0, r.stdout + r.stderr);
const out = JSON.parse(r.stdout);
assert.equal(out.brief_path, join(dir, 'brief.md'));
assert.equal(out.plan_command, `/trekplan --project ${dir}`);
const missing = resolveApprove(join(dir, 'nope'));
assert.equal(missing.status, 1);
assert.match(JSON.parse(missing.stdout).error, /no brief/i);
} finally { rmSync(dir, { recursive: true, force: true }); }
});
test('M2: the Phase 4h stamp line stamps the resolved brief file, whatever its name', () => {
const dir = tmpDir();
const data = tmpDir();
try {
const p = join(dir, 'x-brief.md');
writeFileSync(p, BRIEF);
const line = commandLine('commands/trekbrief.md', '--stamp');
assert.ok(line.includes('{BRIEF_PATH}'), 'the stamp must take the resolved brief path, not {PROJECT_DIR}/brief.md');
const r = run(line, { '{BRIEF_PATH}': p }, { CLAUDE_PLUGIN_DATA: data });
assert.equal(r.status, 0, r.stdout + r.stderr);
assert.equal(gate(p).status, 0);
} finally {
rmSync(dir, { recursive: true, force: true });
rmSync(data, { recursive: true, force: true });
}
});
test('M2: the gate names a remedy that works for --brief too (/trekbrief --approve {brief_path})', () => {
const text = read('commands/trekplan.md');
const section = text.slice(text.indexOf('### Read the brief'), text.indexOf('Read the brief file and parse'));
assert.match(section, /\/trekbrief --approve \{brief_path\}/);
assert.doesNotMatch(section, /\/trekbrief --approve \{project_dir\}/);
const dir = tmpDir();
try {
const p = join(dir, 'brief.md');
writeFileSync(p, BRIEF);
const e = JSON.parse(gate(p).stdout).errors[0];
assert.match(e.hint, /brief-file/);
} finally { rmSync(dir, { recursive: true, force: true }); }
assert.match(read('docs/HANDOVER-CONTRACTS.md'), /--approve <project-dir \| brief-file>/);
});
// ---- M4: the halt sentence is pinned where it acts, not anywhere in a 2500-char window ----
// (PM mutant: the first "**halt**" → "continue anyway" survived the window regex above)
test('M4: the sentence right after the gate command halts on any non-zero exit', () => {
const text = read('commands/trekplan.md');
const gateIdx = text.indexOf(commandLine('commands/trekplan.md', '--check'));
const fenceEnd = text.indexOf('```', gateIdx);
const after = text.slice(fenceEnd + 3).replace(/^\s+/, '');
assert.match(after,
/^Exit 0 → continue\. Any other exit → \*\*halt\*\* — do not read further, spawn no\s+agent, write no plan\./,
'the first sentence after the gate command must be the halt rule, verbatim');
});
test('M4: a check that could not run halts too (never a pass)', () => {
const text = read('commands/trekplan.md');
const section = text.slice(text.indexOf('### Read the brief'), text.indexOf('Read the brief file and parse'));
assert.match(section,
/does not print the JSON report[\s\S]{0,120}\*\*halt\*\* as well[\s\S]{0,60}approval check that could not run is never a pass/);
assert.doesNotMatch(section, /continue anyway/i);
});
// ---- M3 + M7: the header says only what is true ----
test('M3: the header does not claim a reflow keeps the approval; joining lines is STALE (fails safe)', async () => {
const src = readFileSync(MODULE, 'utf8');
const header = src.slice(0, src.indexOf('\nimport '));
assert.doesNotMatch(header, /Reflowing a paragraph keeps the approval/);
assert.match(header, /joining or splitting lines[\s\S]{0,80}stale/i);
const { stampIntentApproval, checkIntentApprovalContent } = await load();
const stamped = stampIntentApproval(BRIEF, STAMP_AT).text;
const joined = stamped.replace('glares.\nThey want', 'glares. They want');
assert.deepEqual(checkIntentApprovalContent(joined).errors.map((e) => e.code), ['BRIEF_INTENT_APPROVAL_STALE']);
});
test('M7: the trekreview exemption is documented as the same trust class as self-stamping', () => {
const src = readFileSync(MODULE, 'utf8');
const header = src.slice(0, src.indexOf('\nimport '));
assert.match(header, /type: trekreview[\s\S]{0,400}same trust class as self-stamping/);
assert.match(read('docs/HANDOVER-CONTRACTS.md'), /`trekreview` briefs are exempt[^.]*\.[^.]*same trust class as self-stamping/);
const plan = read('commands/trekplan.md');
const section = plan.slice(plan.indexOf('### Read the brief'), plan.indexOf('Read the brief file and parse'));
assert.match(section, /same trust class as self-stamping/);
});