diff --git a/.claude/settings.json b/.claude/settings.json index 8792fcf..0ce3077 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -50,67 +50,111 @@ { "matcher": "*", "hooks": [ - { "type": "command", "command": "node tools/enforce-floor.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/enforce-floor.mjs", + "timeout": 10 + } ] }, { "matcher": "Edit|Write|MultiEdit|NotebookEdit|Bash", "hooks": [ - { "type": "command", "command": "node tools/enforce-domain-skill-discipline.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/enforce-domain-skill-discipline.mjs", + "timeout": 10 + } ] }, { "matcher": "*", "hooks": [ - { "type": "command", "command": "node tools/enforce-supreme-gate.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/enforce-supreme-gate.mjs", + "timeout": 10 + } ] }, { "matcher": "*", "hooks": [ - { "type": "command", "command": "node tools/enforce-snapshot.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/enforce-snapshot.mjs", + "timeout": 10 + } ] }, { "matcher": "Edit|Write|MultiEdit|NotebookEdit", "hooks": [ - { "type": "command", "command": "node tools/enforce-normative-content-rules.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/enforce-normative-content-rules.mjs", + "timeout": 10 + } ] }, { "matcher": "Read", "hooks": [ - { "type": "command", "command": "node tools/enforce-read-path-deny.mjs", "timeout": 5 } + { + "type": "command", + "command": "node tools/enforce-read-path-deny.mjs", + "timeout": 5 + } ] }, { "matcher": "mcp__.*", "hooks": [ - { "type": "command", "command": "node tools/enforce-mcp-classification.mjs", "timeout": 5 } + { + "type": "command", + "command": "node tools/enforce-mcp-classification.mjs", + "timeout": 5 + } ] }, { "matcher": "Bash", "hooks": [ - { "type": "command", "command": "node tools/enforce-verify-gate.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/enforce-verify-gate.mjs", + "timeout": 10 + } ] }, { "matcher": "Bash", "hooks": [ - { "type": "command", "command": "node tools/enforce-criterion-gate.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/enforce-criterion-gate.mjs", + "timeout": 10 + } ] }, { "matcher": "TodoWrite", "hooks": [ - { "type": "command", "command": "node tools/enforce-todowrite-skill-verifier.mjs", "timeout": 5 } + { + "type": "command", + "command": "node tools/enforce-todowrite-skill-verifier.mjs", + "timeout": 5 + } ] }, { "matcher": "AskUserQuestion", "hooks": [ - { "type": "command", "command": "node tools/askuser-cosmetic-detector.mjs", "timeout": 5 } + { + "type": "command", + "command": "node tools/askuser-cosmetic-detector.mjs", + "timeout": 5 + } ] } ], @@ -136,54 +180,110 @@ { "matcher": "Write", "hooks": [ - { "type": "command", "command": "node tools/enforce-mentor-then-judge.mjs", "timeout": 300 } + { + "type": "command", + "command": "node tools/enforce-mentor-then-judge.mjs", + "timeout": 300 + } ] }, { "matcher": "Skill", "hooks": [ - { "type": "command", "command": "node tools/enforce-skill-journaler.mjs", "timeout": 5 } + { + "type": "command", + "command": "node tools/enforce-skill-journaler.mjs", + "timeout": 5 + } ] }, { "matcher": "AskUserQuestion", "hooks": [ - { "type": "command", "command": "node tools/enforce-askuser-answer-parser.mjs", "timeout": 5 } + { + "type": "command", + "command": "node tools/enforce-askuser-answer-parser.mjs", + "timeout": 5 + } ] }, { "matcher": "Edit|Write|MultiEdit|Bash|PowerShell", "hooks": [ - { "type": "command", "command": "node tools/enforce-floor-escape-consume.mjs", "timeout": 5 } + { + "type": "command", + "command": "node tools/enforce-floor-escape-consume.mjs", + "timeout": 5 + } ] } ], "Stop": [ { "hooks": [ - { "type": "command", "command": "node tools/observer-stop-hook.mjs", "timeout": 60 } + { + "type": "command", + "command": "node tools/observer-stop-hook.mjs", + "timeout": 60 + } ] }, { "hooks": [ - { "type": "command", "command": "node tools/cost-stop-hook.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/cost-stop-hook.mjs", + "timeout": 10 + } ] }, { "hooks": [ - { "type": "command", "command": "node tools/enforce-coverage-verify.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/enforce-coverage-verify.mjs", + "timeout": 10 + } + ] + }, + { + "hooks": [ + { + "type": "command", + "command": "node tools/enforce-verdict-ack.mjs", + "timeout": 10 + } ] } ], "SessionStart": [ { "hooks": [ - { "type": "command", "command": "node tools/floor-manifest-check.mjs", "timeout": 10 } + { + "type": "command", + "command": "node tools/floor-manifest-check.mjs", + "timeout": 10 + } ] }, { "hooks": [ - { "type": "command", "command": "node tools/router-embedding-warmup.mjs", "timeout": 30 } + { + "type": "command", + "command": "node tools/router-embedding-warmup.mjs", + "timeout": 30 + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "node tools/enforce-verdict-surface.mjs", + "timeout": 10 + } ] } ] diff --git a/tools/enforce-judge-gate.mjs b/tools/enforce-judge-gate.mjs index fe7264a..c45f524 100644 --- a/tools/enforce-judge-gate.mjs +++ b/tools/enforce-judge-gate.mjs @@ -14,6 +14,8 @@ * без транспорта НЕ кирпичит сессию). Рекомендация §11: обкатка в shadow перед block. */ import { readStdin, parseEventJson, exitDecision, runtimeDir } from './enforce-hook-helpers.mjs'; +import { classifyJudgeOutcome } from './verdict-outcome-line.mjs'; +import { pushVerdict } from './verdict-surface-store.mjs'; import { judgeGateMode, judgeActive, resolveJudgeLlmKey } from './judge-gate-config.mjs'; import { finalGate, logVerdict } from './judge-orchestrator.mjs'; import { runJudge, requiredLensesFor, buildJudgePrompt } from './judge-engine.mjs'; @@ -305,6 +307,18 @@ export async function runJudgeTurn(event, { mode, logImpl = logVerdictLine, warn errorType: verdict && verdict.errorType, sealResult, }); + // SP1: громкая видимость вердикта судьи (best-effort, fail-quiet). + if (judged) { + try { + pushVerdict((event && event.session_id) || 'unknown', { + outcome: classifyJudgeOutcome(verdict), + gate: 'judge', + round: (verdict && verdict.round) ?? null, + version: (verdict && verdict.version) ?? null, + reason: (verdict && (verdict.reason || (verdict.verdict && verdict.verdict.recommendation))) || '', + }); + } catch { /* fail-quiet */ } + } if (mode === 'shadow') return { block: false }; // D28: логирует, не блокирует // Способ B + §9 (degraded): live-block, судья не дозвонился (нет ключа/таймаут) → контроллер // ИНФОРМИРУЕТСЯ «судья не дозвонился» (не тихо), печати нет. Это НЕ NO-GO (escalation не растёт). diff --git a/tools/enforce-mentor-on-plan-write.mjs b/tools/enforce-mentor-on-plan-write.mjs index 828a2d1..1a552ee 100644 --- a/tools/enforce-mentor-on-plan-write.mjs +++ b/tools/enforce-mentor-on-plan-write.mjs @@ -33,6 +33,8 @@ import { bumpMentorNoGo, MENTOR_ESCALATE_AFTER } from './mentor-nogo-counter.mjs // Способ B (Фаза 2): наставник НЕ печатает — на GO лишь записывает подписанное одобрение // (mentor-GO, привязка к plan_hash). Печать делает судья (хук ПОСЛЕ наставника) при валидном mentor-GO. import { buildMentorGo, persistMentorGo } from './mentor-go-store.mjs'; +import { classifyJudgeOutcome } from './verdict-outcome-line.mjs'; +import { pushVerdict } from './verdict-surface-store.mjs'; /** * Волна 7 (§6): сообщение арбитража при 3 NO-GO наставника — дословное замечание + @@ -211,6 +213,16 @@ async function main() { registryImpl: () => { try { return loadRegistry({ useCache: false }); } catch { return null; } }, }); if (res && res.ran) { + // SP1: громкая видимость вердикта наставника (best-effort, fail-quiet). + try { + pushVerdict(sess, { + outcome: classifyJudgeOutcome({ wired: res.wired, decision: res.verdict && res.verdict.decision }), + gate: 'mentor', + round: null, + version: null, + reason: (res.verdict && res.verdict.recommendation) || res.reason || '', + }); + } catch { /* fail-quiet */ } // Р7/§3.4: счётчик L1 растёт на NO-GO = содержательный decision='NO-GO' ИЛИ сломанный // вердикт (ok!==true). degraded (wired:false) не считается (escalation L1 не растёт). const verdictDecision = res.verdict && res.verdict.decision;