// tests/lib/guards-prose-pins.test.mjs // Prose pins owned by the guards process (wave-1 order 4). Counted as doc pins // by lib/util/test-census.mjs (PIN_FILE_RE matches `prose-pins.test.mjs`). // // The mod precondition: a Claude Code mod that handles `tool.check` answers // AFTER the rules and the PreToolUse hooks, and «its answer can replace // theirs» — it can approve a call a plugin PreToolUse hook blocked // (code.claude.com/docs/en/permissions § Extend permissions with hooks, // verified verbatim 06.10). So Voyage's stop guards are deterministic only // under a stated condition, and the condition is written where the guards are // described: README § Security hardening, SECURITY.md, and the head of every // PreToolUse stop guard. import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { readFileSync } from 'node:fs'; import { join, dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..'); const read = (rel) => readFileSync(join(ROOT, rel), 'utf8'); export const MOD_PRECONDITION = "Voyage's guards are deterministic only when no installed mod handles `tool.check`."; const STOP_GUARDS = [ 'hooks/scripts/pre-bash-executor.mjs', 'hooks/scripts/pre-write-executor.mjs', 'hooks/scripts/pre-agent-cap.mjs', 'hooks/scripts/pre-plan-gate.mjs', ]; test('mod precondition: README § Security hardening states it', () => { const md = read('README.md'); const start = md.indexOf('### Security hardening'); assert.ok(start !== -1); const end = md.indexOf('\n### ', start + 1); assert.ok(md.slice(start, end).includes(MOD_PRECONDITION), 'README § Security hardening must state the mod precondition'); }); test('mod precondition: SECURITY.md states it', () => { assert.ok(read('SECURITY.md').includes(MOD_PRECONDITION)); }); test('mod precondition: the head of every PreToolUse stop guard states it', () => { for (const rel of STOP_GUARDS) { const head = read(rel).split('\n').slice(0, 40).join('\n').replace(/^\/\/ ?/gm, '').replace(/\n/g, ' '); assert.ok(head.replace(/\s+/g, ' ').includes(MOD_PRECONDITION), `${rel}: the first 40 lines must state the mod precondition`); } }); test('mod precondition: the guards cannot see a mod; the docs say so where it is stated', () => { // Looked up 06.10: no hook input field names installed mods; the documented // ways to see them are `/plugin` and a CLI subcommand, both for a person. assert.match(read('SECURITY.md'), /cannot tell whether such a mod is installed/); }); test('P12 guard: README names the classes it asks about and that it holds in auto mode', () => { const md = read('README.md'); const sec = md.slice(md.indexOf('### Security hardening'), md.indexOf('#### Recommended: disable Skill shell execution')); for (const w of ['merge into `main`', 'push', 'tag', 'destructive git', 'auto mode']) { assert.ok(sec.includes(w), `README § Security hardening must mention ${w}`); } }); test('borrowings are credited where the guards are described (PLAN fixed limit 3)', () => { const md = read('README.md'); const sec = md.slice(md.indexOf('### Security hardening'), md.indexOf('#### Recommended: disable Skill shell execution')); for (const src of ['Superpowers', 'agent-skills', 'ai-native-sdlc', 'Pocock', 'Kiro', 'Cherny', 'Claude Code']) { assert.ok(sec.includes(src), `credit missing for ${src}`); } });