397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
77 lines
5.9 KiB
JavaScript
77 lines
5.9 KiB
JavaScript
// tools/freeze-gate.test.mjs
|
||
import { describe, it, expect } from 'vitest';
|
||
import { freezeGate } from './freeze-gate.mjs';
|
||
|
||
const goodVerdict = { plan_points_addressed: ['a'], reasoning: 'r', recommendation: 'rec', confidence: 0.8, decision: 'GO', plan_hash: 'PH1' };
|
||
// hasUnresolvedExtractedImpl: true = ЕСТЬ неразрешённая EXTRACTED = ГРЯЗНО = блок. Стаб вместо A.
|
||
const verityClean = () => false;
|
||
const verityDirty = () => true;
|
||
const artifactWithExtracted = [{ kind: 'EXTRACTED', claim: 'c', ref: 'f:1' }];
|
||
|
||
describe('freezeGate (CD-3 предусловие)', () => {
|
||
it('валидный вердикт (wired, plan_hash совпал) + чистый контекст с EXTRACTED → pass', () => {
|
||
const r = freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: artifactWithExtracted, hasUnresolvedExtractedImpl: verityClean });
|
||
expect(r.pass).toBe(true);
|
||
});
|
||
it('нет/невалидный вердикт → блок (VA-8)', () => {
|
||
const r = freezeGate({ mentorVerdict: { reasoning: '', plan_hash: 'PH1' }, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: artifactWithExtracted, hasUnresolvedExtractedImpl: verityClean });
|
||
expect(r.pass).toBe(false);
|
||
expect(r.reason).toMatch(/вердикт/i);
|
||
});
|
||
it('вердикт wired:false → блок (SE-R6-6 не суд)', () => {
|
||
const r = freezeGate({ mentorVerdict: goodVerdict, mentorWired: false, planHash: 'PH1', verifiedContextArtifact: artifactWithExtracted, hasUnresolvedExtractedImpl: verityClean });
|
||
expect(r.pass).toBe(false);
|
||
});
|
||
it('неразрешённая EXTRACTED в контексте → блок печати (✅O2)', () => {
|
||
const r = freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: artifactWithExtracted, hasUnresolvedExtractedImpl: verityDirty });
|
||
expect(r.pass).toBe(false);
|
||
expect(r.reason).toMatch(/EXTRACTED|verity|цитат/i);
|
||
});
|
||
it('A1 (нах.F4): verdict.plan_hash ≠ planHash → блок (stale/чужой вердикт)', () => {
|
||
const r = freezeGate({ mentorVerdict: { ...goodVerdict, plan_hash: 'СТАРЫЙ' }, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: artifactWithExtracted, hasUnresolvedExtractedImpl: verityClean });
|
||
expect(r.pass).toBe(false);
|
||
expect(r.reason).toMatch(/привязан|F4/i);
|
||
});
|
||
it('Д-2а (VA-9/SE-A3): пустой артефакт (0 EXTRACTED) → блок даже при чистом impl', () => {
|
||
const r = freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: [], hasUnresolvedExtractedImpl: verityClean });
|
||
expect(r.pass).toBe(false);
|
||
expect(r.reason).toMatch(/VA-9|EXTRACTED/i);
|
||
});
|
||
it('Д-2а: артефакт только из INFERRED (0 EXTRACTED) → блок (VA-9)', () => {
|
||
const r = freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: [{ kind: 'INFERRED', derivation_ref: 'd' }], hasUnresolvedExtractedImpl: verityClean });
|
||
expect(r.pass).toBe(false);
|
||
});
|
||
it('F-C4 (sharp-edges, ужесточение A1): planHash не передан (null/"") → блок — печать без binding запрещена', () => {
|
||
const noHash = freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: null, verifiedContextArtifact: artifactWithExtracted, hasUnresolvedExtractedImpl: verityClean });
|
||
expect(noHash.pass).toBe(false);
|
||
expect(noHash.reason).toMatch(/planHash|binding|F-C4/i);
|
||
const emptyHash = freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: '', verifiedContextArtifact: artifactWithExtracted, hasUnresolvedExtractedImpl: verityClean });
|
||
expect(emptyHash.pass).toBe(false);
|
||
});
|
||
it('fail-CLOSED: hasUnresolvedExtractedImpl бросил / не функция → блок', () => {
|
||
const boom = () => { throw new Error('сбой'); };
|
||
expect(freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: artifactWithExtracted, hasUnresolvedExtractedImpl: boom }).pass).toBe(false);
|
||
expect(freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: artifactWithExtracted }).pass).toBe(false);
|
||
});
|
||
|
||
// FR-1 (финревью 2026-06-11): SE-A3-обязательство context-verity:93-94 закрыто В ГЕЙТЕ —
|
||
// VF-1 и SE-A1 не должны проходить печать даже при 1 честной EXTRACTED.
|
||
it('FR-1/VF-1: INFERRED без derivation_ref рядом с честной EXTRACTED → блок', () => {
|
||
const art = [...artifactWithExtracted, { kind: 'INFERRED', claim: 'догадка', derivation_ref: '' }];
|
||
const r = freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: art, hasUnresolvedExtractedImpl: verityClean });
|
||
expect(r.pass).toBe(false);
|
||
expect(r.reason).toMatch(/VF-1|derivation/i);
|
||
});
|
||
it('FR-1/SE-A1: неизвестный kind рядом с честной EXTRACTED → блок (третья полоса обхода)', () => {
|
||
const art = [...artifactWithExtracted, { kind: 'VERIFIED', claim: 'самозванец' }];
|
||
const r = freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: art, hasUnresolvedExtractedImpl: verityClean });
|
||
expect(r.pass).toBe(false);
|
||
expect(r.reason).toMatch(/SE-A1|kind/i);
|
||
});
|
||
it('FR-1: валидный mix (EXTRACTED + INFERRED с derivation_ref) → pass', () => {
|
||
const art = [...artifactWithExtracted, { kind: 'INFERRED', claim: 'вывод', derivation_ref: 'из f:1' }];
|
||
const r = freezeGate({ mentorVerdict: goodVerdict, mentorWired: true, planHash: 'PH1', verifiedContextArtifact: art, hasUnresolvedExtractedImpl: verityClean });
|
||
expect(r.pass).toBe(true);
|
||
});
|
||
});
|