Files
portal/tools/seal-log.test.mjs
T
Дмитрий 9d8d3de782 feat(mentor): degraded-судья диагностируем — cause(no_key/transport_error)+errorType+at
Разбор «перемежающегося degraded судьи» по systematic-debugging: действующего
бага нет (ключ SET, 28/28 вердиктов чистые, degraded-строки несверяемы — at:null,
без парного WARN). Гипотеза «retry/таймаут» не подтверждена → таймаут не трогали.

Вместо этого закрыта слепота диагностики (TDD, под maintenance):
- callJudgeModel различает no_key vs transport_error+errorType (classifyLLMError);
- причина протекает в вердикт → warnJudgeUnavailable (+cause/error_type/at) и seal-запись;
- main() передаёт nowMs: Date.now() → seal/verdict/warn больше не at:null (логи сверяемы).

Файлы: tools/seal-log.mjs, tools/enforce-judge-gate.mjs. +9 тестов; 2 exact-match
приведены к новому контракту. Регрессия tools-only 3829 GREEN (база 3820), 0 регрессий.
cspell-words.txt +8 терминов. Роадмап: секция «Печать M7» + degraded-наблюдаемость.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 04:12:53 +03:00

68 lines
3.2 KiB
JavaScript

import { describe, it, expect } from 'vitest';
import { buildSealEntry } from './seal-log.mjs';
describe('buildSealEntry — наблюдаемость попыток печати (M7 seal observability)', () => {
it('судья no-op (не активен) → kind seal_attempt, judge_active:false, sealed:false, reason', () => {
const e = buildSealEntry({ functionName: 'gate1', judgeActive: false });
expect(e.kind).toBe('seal_attempt');
expect(e.judge_active).toBe(false);
expect(e.wired).toBe(false);
expect(e.seal_attempted).toBe(false);
expect(e.sealed).toBe(false);
expect(typeof e.reason).toBe('string');
});
it('вердикт не wired (degraded/unavailable) → seal_attempted:false, reason про wired', () => {
const e = buildSealEntry({ functionName: 'gate2', judgeActive: true, wired: false, decision: 'GO' });
expect(e.judge_active).toBe(true);
expect(e.wired).toBe(false);
expect(e.decision).toBe('GO');
expect(e.seal_attempted).toBe(false);
expect(e.sealed).toBe(false);
expect(e.reason).toMatch(/wired/i);
});
it('wired GO + печать встала → seal_attempted:true, sealed:true', () => {
const e = buildSealEntry({ functionName: 'gate1', judgeActive: true, wired: true, decision: 'GO', sealResult: { sealed: true, kind: 'artifact' } });
expect(e.wired).toBe(true);
expect(e.seal_attempted).toBe(true);
expect(e.sealed).toBe(true);
expect(e.kind_sealed).toBe('artifact');
});
it('wired GO, но печать НЕ встала → sealed:false + дословный reason из sealResult', () => {
const e = buildSealEntry({ functionName: 'gate2', judgeActive: true, wired: true, decision: 'GO', sealResult: { sealed: false, kind: 'plan', reason: 'mentor freeze-gate: нет pass' } });
expect(e.seal_attempted).toBe(true);
expect(e.sealed).toBe(false);
expect(e.reason).toBe('mentor freeze-gate: нет pass');
});
it('wired NO-GO → печать не положена, seal_attempted:false', () => {
const e = buildSealEntry({ functionName: 'gate2', judgeActive: true, wired: true, decision: 'NO-GO' });
expect(e.wired).toBe(true);
expect(e.decision).toBe('NO-GO');
expect(e.seal_attempted).toBe(false);
expect(e.sealed).toBe(false);
expect(e.reason).toMatch(/NO-GO/);
});
it('форма стабильна: всегда есть kind/functionName/at-поля', () => {
const e = buildSealEntry({});
expect(e.kind).toBe('seal_attempt');
expect('functionName' in e).toBe(true);
expect('at' in e).toBe(true);
});
it('degraded c причиной → cause/error_type заполнены, reason содержит cause + слово wired', () => {
const e = buildSealEntry({ functionName: null, judgeActive: true, wired: false, decision: 'GO', cause: 'transport_error', errorType: 'timeout' });
expect(e.cause).toBe('transport_error');
expect(e.error_type).toBe('timeout');
expect(e.reason).toMatch(/transport_error/);
expect(e.reason).toMatch(/wired/i);
});
it('at прокидывается значением (не только присутствует)', () => {
expect(buildSealEntry({ judgeActive: false, nowMs: 12345 }).at).toBe(12345);
});
});