merge: wave 1 order 4 (guards) into main

CLAUDE.md: the review paragraph (order 5) and the guards paragraph
(order 4) are both kept (planned union line).

The launch gate went red on the merged tree: post-approval-log reads
the session's permission_mode, and its regression test feeds that value.
Both were untracked when the branch ran its suite, so the gate never saw
them there. Operator decision 2026-10-06: a counted exemption in a new
class (mode-read: 3 + 1 hits), and the binding-rule sentence in CLAUDE.md
names it. An extra hit in either file turns the gate red again (checked).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-06 20:41:21 +02:00
commit 47864f91b4
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
32 changed files with 3284 additions and 110 deletions

View file

@ -65,6 +65,8 @@ const EXEMPT = new Map([
['tests/fixtures/synthesis/exploration/dependency-tracer.md', ['c', 2, 'recorded exploration fixture']],
['docs/proevesett.md', ['operator-instrument', 3, "the operator's own eval run through Anthropic's `claude plugin eval`; Voyage starts nothing - kept by operator decision 2026-10-02"]],
['.gitignore', ['operator-instrument', 1, 'ignores the output of that same operator-run eval']],
['hooks/scripts/post-approval-log.mjs', ['mode-read', 3, "reads the session's permission_mode from hook input so an approval is never logged when no person was asked; starts nothing - operator decision 2026-10-06"]],
['tests/hooks/guards-review-regressions.test.mjs', ['mode-read', 1, 'feeds that same permission_mode value to the hook under test; starts nothing']],
]);
function trackedFiles(root) {
@ -137,7 +139,7 @@ test('every EXEMPT entry is tracked, has a reason, and hits exactly its recorded
const files = new Set(trackedFiles(ROOT));
for (const [rel, [cls, count, reason]] of EXEMPT) {
assert.ok(files.has(rel), `EXEMPT names untracked ${rel}`);
assert.ok(['c', 'self', 'operator-instrument'].includes(cls), `${rel}: unknown class ${cls}`);
assert.ok(['c', 'self', 'operator-instrument', 'mode-read'].includes(cls), `${rel}: unknown class ${cls}`);
assert.ok(reason && reason.length > 10, `${rel}: an exemption needs a reason`);
const n = scan(ROOT, [rel]).length;
assert.equal(n, count, `${rel}: ${n} hit(s), EXEMPT records ${count} - a new hit is a launch, a lost one is a stale entry`);