b739d5adad
Болезни 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>
40 lines
2.4 KiB
JavaScript
40 lines
2.4 KiB
JavaScript
#!/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') : '';
|
||
}
|