Files
brain/tools/m7-phase4-invariants.test.mjs
T

66 lines
2.9 KiB
JavaScript

// tools/m7-phase4-invariants.test.mjs
/**
* §12 CI-инварианты Фазы 4 (М7): анти-регресс поглощённой дисциплины. Lock-in: красит,
* если кто-то вернёт fail-open / override-вокабуляр / text-bypass в поглощённые хуки.
* Scope — ТОЛЬКО поглощённая дисциплина 4a/4b (coverage/todowrite/rationalization). НЕ
* ещё-не-поглощённые (memory-coverage/branch-switch/verify-before-push) — те retire в Ф8.
*/
import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
import { FAIL_CLOSE_DISCIPLINE_HOOKS } from './enforce-hook-helpers.mjs';
const here = dirname(fileURLToPath(import.meta.url));
// Поглощённая дисциплина Фазы 4a/4b с собственным main (Stop/Pre-хуки).
const ABSORBED_PHASE4 = [
'enforce-coverage-verify',
'enforce-todowrite-skill-verifier',
'enforce-rationalization-audit',
];
function src(name) {
return readFileSync(join(here, `${name}.mjs`), 'utf8');
}
// FAIL_OPEN: catch-блок, молча возвращающий block:false (анти-SE2).
const FAIL_OPEN_RE = /catch\s*(?:\([^)]*\))?\s*\{[^}]*block:\s*false/s;
// TEXT-BYPASS: контроллер-текст → пропуск (assistantText.includes(...) ветка allow).
const TEXT_BYPASS_RE = /assistantText\.includes\(/;
describe('§12 Фаза 4 — fail-CLOSE (анти-SE2): поглощённая дисциплина не fail-open', () => {
for (const name of ABSORBED_PHASE4) {
it(`${name} использует exitDisciplineDecision`, () => {
expect(src(name).includes('exitDisciplineDecision')).toBe(true);
});
it(`${name} НЕ содержит fail-open catch → block:false`, () => {
expect(FAIL_OPEN_RE.test(src(name))).toBe(false);
});
}
});
describe('§12 Фаза 4 — escape≠override: дисциплина не несёт override-вокабуляра', () => {
for (const name of ABSORBED_PHASE4) {
it(`${name} не вызывает findOverride (только escape М6 — дверь)`, () => {
expect(src(name).includes('findOverride(')).toBe(false);
});
}
});
describe('§12 Фаза 4 — нет controller-text → allow (text-bypass вырезан, Класс 1)', () => {
for (const name of ABSORBED_PHASE4) {
it(`${name} не пропускает по assistantText.includes(marker)`, () => {
expect(TEXT_BYPASS_RE.test(src(name))).toBe(false);
});
}
});
describe('§12 Фаза 4 — манифест: поглощённые хуки числятся в FAIL_CLOSE_DISCIPLINE_HOOKS', () => {
for (const name of [...ABSORBED_PHASE4, 'enforce-self-debrief-detector']) {
it(`${name} в манифесте fail-CLOSE (Ф6 self-check)`, () => {
expect(FAIL_CLOSE_DISCIPLINE_HOOKS).toContain(name);
});
}
});