Files
brain/tools/brain-retro-opus-reviewer.test.mjs
T
2026-06-15 14:16:14 +03:00

127 lines
5.7 KiB
JavaScript

// tools/brain-retro-opus-reviewer.test.mjs — TDD for Phase 3 Task 18 (G16, spec §4.6)
import { describe, it, expect } from 'vitest';
import { buildReviewPrompt, buildReviewPromptStructured, parseReview } from './brain-retro-opus-reviewer.mjs';
describe('buildReviewPrompt — adaptive v2/v3/v4 (spec §4.6)', () => {
it('v4 includes alternatives_considered + self_assessment + chain_gaps cues', () => {
const ep = {
schema_version: 4,
schema_minor: 2,
task_id: 't',
primary_rationale: { task_classification: 'feature', node_chosen: 'direct' },
classifier_output: { recommended_node: '#19', alternatives_considered: [{ node: 'x', match_score: 0.5 }] },
self_assessment: { summary: 'ok', confidence_in_choice: 0.8 },
execution_trace: { chain_gaps: [] },
};
const p = buildReviewPrompt(ep);
expect(p).toContain('alternatives_considered');
expect(p).toContain('self_assessment');
expect(p).toContain('chain_gaps');
});
it('structured prompt returns a non-empty system block (caching target)', () => {
const { system, user } = buildReviewPromptStructured({ schema_version: 2 });
expect(typeof system).toBe('string');
expect(system.length).toBeGreaterThan(0);
expect(typeof user).toBe('string');
});
it('v3 omits alternatives_considered cue', () => {
expect(buildReviewPrompt({ schema_version: 3 })).not.toContain('alternatives_considered');
});
it('v2 omits alternatives + post-hoc self_assessment notes', () => {
const p = buildReviewPrompt({ schema_version: 2 });
expect(p).not.toContain('alternatives_considered');
// The "agent_self_assessment_accuracy" cue is part of the 8-dim contract
// (always present). What v2 must NOT have is the adaptive note that
// tells the reviewer to compare honesty against a post-hoc field — v2
// episodes do not carry one.
expect(p).not.toMatch(/self_assessment\s*\(if present/);
expect(p).not.toContain('post-hoc judgement');
});
it('includes the episode JSON verbatim for the reviewer to read', () => {
const ep = { schema_version: 4, task_id: 'task-xyz-1' };
expect(buildReviewPrompt(ep)).toContain('task-xyz-1');
});
});
describe('buildReviewPromptStructured — split for prompt caching', () => {
it('returns { system, user } with episode JSON only in user block', () => {
const ep = { schema_version: 4, task_id: 'task-cache-1' };
const { system, user } = buildReviewPromptStructured(ep);
expect(typeof system).toBe('string');
expect(typeof user).toBe('string');
expect(user).toContain('task-cache-1');
expect(system).not.toContain('task-cache-1');
});
it('system block carries the 8-dim cue contract', () => {
const { system } = buildReviewPromptStructured({ schema_version: 4 });
expect(system).toContain('node_quality');
expect(system).toContain('chain_quality');
expect(system).toContain('outcome_reviewed');
expect(system).toContain('reasoning');
});
it('system block is identical across two v4 episodes with different bodies (cache prerequisite)', () => {
const a = buildReviewPromptStructured({ schema_version: 4, task_id: 'a' }).system;
const b = buildReviewPromptStructured({ schema_version: 4, task_id: 'b', other: 'noise' }).system;
expect(a).toBe(b);
});
it('user block carries Episode (JSON): marker + final "Output JSON only."', () => {
const { user } = buildReviewPromptStructured({ schema_version: 4, task_id: 't' });
expect(user).toContain('Episode (JSON):');
expect(user.trim().endsWith('Output JSON only.')).toBe(true);
});
it('buildReviewPrompt remains a string concatenation of system + user (backward compat)', () => {
const ep = { schema_version: 4, task_id: 'compat-1' };
const { system, user } = buildReviewPromptStructured(ep);
const combined = buildReviewPrompt(ep);
expect(combined).toContain(system);
expect(combined).toContain(user);
});
});
describe('parseReview — 8-dim review schema (spec §4.6)', () => {
it('parses a complete 8-dim review JSON', () => {
const r = parseReview('{"node_quality":"correct","chain_quality":"n/a","gap_assessment":"n/a","agent_self_assessment_accuracy":"accurate","error_root_cause":"n/a","alternative_better":null,"outcome_reviewed":"success","reasoning":"x"}');
expect(r.node_quality).toBe('correct');
expect(r.outcome_reviewed).toBe('success');
expect(r.alternative_better).toBeNull();
expect(r.reasoning).toBe('x');
});
it('strips ```json fence', () => {
const r = parseReview('```json\n{"node_quality":"wrong_node","chain_quality":"missing_step","gap_assessment":"acceptable","agent_self_assessment_accuracy":"over_confident","error_root_cause":"wrong_skill","alternative_better":"#19","outcome_reviewed":"rework","reasoning":"y"}\n```');
expect(r.node_quality).toBe('wrong_node');
expect(r.alternative_better).toBe('#19');
});
it('returns null on malformed JSON', () => {
expect(parseReview('not json')).toBeNull();
});
it('returns null when required field missing', () => {
expect(parseReview('{"node_quality":"correct"}')).toBeNull();
});
it('returns reviewer_error passthrough when reviewer escalates', () => {
const r = parseReview('{"reviewer_error":"malformed episode"}');
expect(r?.reviewer_error).toBe('malformed episode');
});
});
describe('buildReviewPromptStructured classifierContext (config-seam §D1)', () => {
it('дефолт → Лидерра', () => {
expect(buildReviewPromptStructured({ schema_version: 4 }).system).toContain('Лидерра');
});
it('classifierContext инъектируется', () => {
expect(buildReviewPromptStructured({ schema_version: 4 }, { classifierContext: 'ProjZ' }).system)
.toContain('ProjZ');
});
});