Files
portal/tools/objection-format.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.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* objection-format — дословные сводки возражений для блок-сообщений (П1).
* Чистые форматтеры (презентация, не логика гейта): судья и наставник.
* Fail-safe: мусор/нет возражений → пустая строка.
*/
/**
* Дословная сводка возражений судьи. Две формы:
* - результат runJudge — блокирующие в `blocking[{anchor:{ref},severity}]` (приоритет);
* - сырой parseJudgeResponse — `objections[{anchor:{ref},severity}]` (fallback).
*/
export function formatJudgeObjection(verdict) {
const blocking = verdict && Array.isArray(verdict.blocking) ? verdict.blocking : null;
const objs = (blocking && blocking.length) ? blocking
: (verdict && Array.isArray(verdict.objections) ? verdict.objections : []);
const lines = objs
.filter((o) => o && o.anchor && o.anchor.ref)
.map((o) => `— [${o.severity || 'light'}] ${o.anchor.ref}`);
return lines.join('\n');
}
/** Дословная сводка замечания наставника (результат onPlanWrite {ok,wired,reason,verdict}).
* Р7/мерж: NO-GO = decision==='NO-GO' ИЛИ сломанный вердикт (ok!==true). Доносит СУТЬ —
* recommendation (что править) + reasoning (разбор) + plan_points_addressed (по пунктам,
* включая скил-конкретику) + reason сбоя. GO → пустая строка (не заворот). */
export function formatMentorObjection(r) {
if (!r || r.wired !== true) return '';
const v = r.verdict || {};
const isNoGo = v.decision === 'NO-GO' || r.ok !== true;
if (!isNoGo) return '';
const lines = ['Замечание наставника:'];
if (typeof v.recommendation === 'string' && v.recommendation.trim()) lines.push(`Что править: ${v.recommendation.trim()}`);
if (typeof v.reasoning === 'string' && v.reasoning.trim()) lines.push(`Разбор: ${v.reasoning.trim()}`);
const pts = Array.isArray(v.plan_points_addressed) ? v.plan_points_addressed.filter((p) => typeof p === 'string' && p.trim()) : [];
if (pts.length) lines.push('По пунктам:', ...pts.map((p) => `${p}`));
if (typeof r.reason === 'string' && r.reason.trim()) lines.push(`(${r.reason.trim()})`);
return lines.length > 1 ? lines.join('\n') : '';
}