Files
portal/tools/objection-delivery.test.mjs
T
Дмитрий b739d5adad feat(mentor): мерж роутера в наставника — единый рецензент (спека+план+скилы) + decision GO/NO-GO
Болезни B (роутер в пустоту) + A (наставник не заворачивал) — лечение Р7/Р8 (Подход 1):
наставник — единый мозг-рецензент, зовёт classify() как функцию (3 слоя + граф nodes.yaml +
карточки — код не тронут, новый вызыватель), судит спеку+план+выбор скилов, заворачивает NO-GO.

- validateMentorVerdict + промпты (план/спека): явное decision GO|NO-GO (поглощённый Р7)
- plan-skills.mjs: parsePlanSkills (skills-json) + extractPlanGoal (зеркало extractGoal судьи)
- mentor-seam: renderSkillContext; onPlanWrite зовёт classifyImpl (fail-safe: сбой → без скил-сверки)
- decideMentorObjection: заворот на decision=NO-GO ИЛИ сломанный вердикт; mentor-GO только на чистом GO
- formatMentorObjection доносит суть (recommendation + reasoning + plan_points), GO -> пусто
- enforce-mentor main: loadRegistry + classify; счётчик L1 decision-aware (Р7/§3.4)
- скил-сверка — только план (gate2); спека (gate1) — по сути + decision
- включает redesign согласования L1->L2 (Фазы 0-6, способ B: наставник->судья->печать)
- регрессия tools-only 3901 passed + 2 skip (база 3877, +24 теста, 0 регрессий)

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

40 lines
2.1 KiB
JavaScript

import { describe, it, expect } from 'vitest';
import { buildObjectionFeedback, buildDegradedFeedback } from './objection-delivery.mjs';
describe('buildObjectionFeedback', () => {
it('собирает полный текст замечания с пометкой стороны (наставник)', () => {
const out = buildObjectionFeedback({ side: 'mentor', text: 'пункт 2 плохой' });
expect(out).toContain('наставник');
expect(out).toContain('пункт 2 плохой');
});
it('пометка судьи для side=judge', () => {
const out = buildObjectionFeedback({ side: 'judge', text: 'шаг 4 трогает порог' });
expect(out).toContain('судья');
expect(out).toContain('шаг 4 трогает порог');
});
it('пустой текст → безопасная заглушка, не пусто', () => {
expect(buildObjectionFeedback({ side: 'judge', text: '' })).toMatch(/судья/);
});
it('не обрезает длинный текст замечания', () => {
const long = 'A'.repeat(5000);
expect(buildObjectionFeedback({ side: 'mentor', text: long })).toContain(long);
});
});
describe('buildDegradedFeedback (спека §9 — degraded ИИ информирует контроллера, не тишина)', () => {
it('наставник degraded → «не смог дозвониться» + причина, помечен наставник', () => {
const out = buildDegradedFeedback({ side: 'mentor', reason: 'timeout' });
expect(out).toContain('наставник');
expect(out).toMatch(/не смог дозвониться|недоступен/i);
expect(out).toContain('timeout');
});
it('судья degraded → помечен судья, печати нет', () => {
const out = buildDegradedFeedback({ side: 'judge', reason: 'no_key' });
expect(out).toContain('судья');
expect(out).toMatch(/печат/i);
});
it('пустая причина → безопасная заглушка, не пусто', () => {
expect(buildDegradedFeedback({ side: 'mentor' })).toMatch(/наставник/);
});
});