397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
55 lines
3.1 KiB
JavaScript
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 — никогда не ломает хук */ }
|
|
}
|