#!/usr/bin/env node /** * seal-log (M7 наблюдаемость печати) — закрывает дыру «провал печати нигде не логируется». * Каждая запись плана/спеки порождает строку в ~/.claude/runtime/seal-attempts.jsonl: * был ли судья активен, был ли вердикт wired, пыталась ли печать, встала ли, и ПОЧЕМУ нет. * Чистый buildSealEntry (детерминирован, тестируется) + best-effort logSealAttempt (I/O). */ import { appendFileSync, mkdirSync } from 'node:fs'; import { join } from 'node:path'; import { runtimeDir } from './enforce-hook-helpers.mjs'; const SEAL_LOG = 'seal-attempts.jsonl'; /** * Чистая сборка записи попытки печати. Печать «пытается» ТОЛЬКО на wired GO; иначе * фиксируем причину пропуска (судья не активен / вердикт не wired / NO-GO / провал печати). */ export function buildSealEntry({ functionName, judgeActive, wired, decision, sealResult, cause = null, errorType = null, nowMs = null } = {}) { const active = judgeActive !== false; // undefined → считаем активным (по умолчанию) const isWired = !!wired; const attempted = isWired && decision === 'GO'; // печать положена только на wired GO const sealed = !!(sealResult && sealResult.sealed === true); let reason = null; if (!active) reason = 'судья не активен (no-op, $0)'; // M7 (2026-06-13): degraded больше не безымянный — причина (no_key / transport_error:<тип>) // протекает в reason, чтобы diagnose «почему судья недоступен» без других логов. else if (!isWired) reason = `вердикт не wired (судья недоступен/degraded${cause ? `: ${cause}${errorType ? `/${errorType}` : ''}` : ''}) — печать не пыталась`; else if (decision === 'NO-GO') reason = 'судья NO-GO — печать не положена'; else if (sealResult && sealResult.sealed !== true) reason = sealResult.reason ?? 'печать не встала (причина не указана)'; return { kind: 'seal_attempt', functionName: functionName ?? null, judge_active: active, wired: isWired, decision: decision ?? null, seal_attempted: attempted, sealed, kind_sealed: sealResult ? (sealResult.kind ?? null) : null, cause: cause ?? null, error_type: errorType ?? null, reason, at: nowMs, }; } /** Best-effort append в ~/.claude/runtime/seal-attempts.jsonl (Node fs, не Write-tool). */ export function logSealAttempt(entry, { fsImpl = { appendFileSync, mkdirSync }, dir = runtimeDir() } = {}) { try { fsImpl.mkdirSync(dir, { recursive: true }); fsImpl.appendFileSync(join(dir, SEAL_LOG), JSON.stringify(entry) + '\n'); } catch { /* наблюдаемость best-effort — никогда не ломает хук */ } }