Files
brain/tools/seal-log.mjs
T

55 lines
3.1 KiB
JavaScript

#!/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 — никогда не ломает хук */ }
}