ae81a52a99
Третий построчный аудит машин 1-4 свежим объективом (корректность логики /
реальные баги — НЕ понимание, НЕ грабли; это были два прошлых прохода).
4 читающих под-агента code-analyzer. M1/M2/M3 — багов ядра нет (подтверждено).
M4 (судья, инертен; код должен быть верен и при включении): 3 реальные дыры по TDD.
M4:
- judge-engine.mjs runJudge: (raw.objections||[]).filter((o)=>o.verdict) падал на
objections=[null] (o.verdict на null) и на не-массиве (.filter is not a function).
|| гасит только falsy. Краш ломал вердикт; в инертной обёртке выброс уходил в
catch→block:false = fail-open. Fix: Array.isArray(...)?...:[] + (o && o.verdict).
- judge-verdict-slots.mjs: String(raw).trim().length скрывал не-строки — слот {}
давал '[object Object]' (длина 15) и проходил как содержательный (мусорный
объект/массив штамповал форму вердикта). Fix: слот обязан быть строкой
(typeof raw !== 'string' → trivial). Мягкий fail-open формы закрыт.
- judge-orchestrator.mjs runGateLadder: step.run() без try/catch пробрасывал
исключение упавшего шага пола вместо «пол не пройден» → решение неопределённо
(в обёртке catch→block:false = fail-open). Fix: бросок шага = passed:false
(fail-closed → блок), последующие не запускаются. Чистый модуль теперь сам
гарантирует безопасную сторону, не полагаясь на обёртку.
Регрессия tools-only 2560 passed + 2 skip (+5 TDD-тестов, 0 регрессий).
Осознанно НЕ менялось (без призраков):
- M1 verifyChain без 3-го арг = нарушение контракта вызова, не валидный вход.
- M2 node-в-цепочке = то же разрешение, что одиночный node (контракт, тест L53);
readonly-git-в-цепочке блок = осознанный default-deny (fail-safe).
- M3 defer уже защищён G-фиксом (if e.status!=='pending' return e — ДО defer);
N3 stale-комментарий (код строже докстринга).
- M4-C DESTRUCTIVE_RE иллюстративен (divergence всё равно судится; разрушительный
bash режется полом M2/M5 до судьи); M4-D slop-counter↔logVerdict — live-wiring.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
25 lines
1.8 KiB
JavaScript
25 lines
1.8 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* judge-verdict-slots (§9.2) — ОТДЕЛЬНЫЙ валидатор формы вердикта (не сам судья,
|
|
* как A0: подсудный не судит себя). Вердикт обязан нести слот на КАЖДУЮ требуемую
|
|
* для гейта линзу; пустой/тривиальный слот → невалидно → ГО не выдаётся. Тут проверяем
|
|
* только ФОРМУ (наличие+нетривиальность); качество мысли внутри слота — не сюда
|
|
* (часть честного потолка ~0.5%, добивают премортем/оценщик/владелец).
|
|
*/
|
|
const DEFAULT_MIN_LEN = 8; // тривиальным считаем слот короче порога (после trim)
|
|
|
|
export function validateVerdictSlots(verdict, requiredLenses = [], { minLen = DEFAULT_MIN_LEN } = {}) {
|
|
const slots = (verdict && typeof verdict === 'object' && verdict.slots) || {};
|
|
const missing = [];
|
|
const trivial = [];
|
|
for (const lens of requiredLenses) {
|
|
const raw = slots[lens];
|
|
if (raw === undefined || raw === null) { missing.push(lens); continue; }
|
|
// Аудит M1-M4 (свежий объектив): слот обязан быть СТРОКОЙ. Раньше String(raw) скрывал
|
|
// не-строки — `{}` давал '[object Object]' (длина 15) и проходил как содержательный,
|
|
// т.е. мусорный объект/массив штамповал слот. Теперь не-строка → тривиально (форма невалидна).
|
|
if (typeof raw !== 'string' || raw.trim().length < minLen) trivial.push(lens);
|
|
}
|
|
return { valid: missing.length === 0 && trivial.length === 0, missing, trivial };
|
|
}
|