feat(mentor): objection-format judge+mentor formatters (waves 4-5) - 9 tests green

This commit is contained in:
Дмитрий
2026-06-12 18:43:43 +03:00
parent 8918190bbe
commit eca9be46c8
3 changed files with 76 additions and 8 deletions
+8 -8
View File
@@ -1,6 +1,6 @@
# Brain Status (auto-generated)
Last updated: 2026-06-12T15:26:26.340Z
Last updated: 2026-06-12T15:30:14.970Z
| Контролёр | Состояние | Детали |
|---|---|---|
@@ -8,7 +8,7 @@ Last updated: 2026-06-12T15:26:26.340Z
| C2 Cross-ref consistency | ✅ | [cross-ref-checker] OK — 0 drift in 4 files |
| C3 Observer-of-observer | ✅ | [observer-of-observer] OK — last read 2 week(s) ago |
| C4 Сигнальный статус | ✅ | This file (self-reference) |
| C5 Observer-coverage | ✅ | 1120 episode(s) this month · Stop-hook + post-commit OK |
| C5 Observer-coverage | ✅ | 1121 episode(s) this month · Stop-hook + post-commit OK |
| C6 Chain map sync | ✅ | [chain-map-checker] OK — 16 chains in sync |
## Кто на посту (оборона М1–М6)
@@ -54,6 +54,7 @@ Last updated: 2026-06-12T15:26:26.340Z
| Время | Действие | Причина |
|---|---|---|
| 2026-06-12T15:30:00.302Z | write:c:/users/administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 | path «C:/Users/Administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 |
| 2026-06-12T15:25:57.082Z | write:c:/users/administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 | path «C:/Users/Administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 |
| 2026-06-12T15:22:21.601Z | write:c:/users/administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 | path «C:/Users/Administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 |
| 2026-06-12T15:17:57.060Z | write:c:/users/administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 | path «C:/Users/Administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 |
@@ -63,12 +64,11 @@ Last updated: 2026-06-12T15:26:26.340Z
| 2026-06-12T15:02:09.672Z | bash:ls $TEMP/claude-economy-196f7d18-2a2c-4f25-966f-ebc43f93e381.json 2>/dev/null && cat $TEMP/claude-economy-196f7d18- | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:ls $TEMP/claude-econom |
| 2026-06-12T15:01:31.586Z | write:c:/users/administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 | path «C:/Users/Administrator/.claude/projects/c---------------------crm-------------/196f7d18-2a2c-4f25-966f-ebc43f93e38 |
| 2026-06-12T15:01:26.432Z | bash:cat "$TEMP/claude-economy-196f7d18-2a2c-4f25-966f-ebc43f93e381.json" 2>/dev/null \|\| echo "FILE_NOT_FOUND" | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:cat "$TEMP/claude-econ |
| 2026-06-12T14:55:22.377Z | bash:ls "$TEMP/claude-economy-196f7d18-2a2c-4f25-966f-ebc43f93e381.json" 2>/dev/null && cat "$TEMP/claude-economy-196f7d | floor: опасная по содержанию команда без аварийного выхода — блок (правило 8); FLOOR-ESCAPE: bash:ls "$TEMP/claude-econo |
## Метрики (информационные, не алерты)
- Observer evidence: 1120 episodes this month, 0 observer_error markers, 5 PII matches before filter
- Legacy v1 episodes (not in factor analysis): 1120
- Observer evidence: 1121 episodes this month, 0 observer_error markers, 5 PII matches before filter
- Legacy v1 episodes (not in factor analysis): 1121
- Last /brain-retro: 16 day(s) ago
- Использование узлов: см. `/brain-retro` (раз в спринт). missed_activations: 0. **Неиспользованные узлы — не алерт, если профильной задачи не было** (Pravila §16.4 v1.36; capability-readiness; см. memory `feedback_brain_unused_tools_not_problem` — outside-repo memory store).
@@ -84,9 +84,9 @@ Baseline дисциплины роутера (этап 2 router discipline overh
| bugfix | 33 | 12.1% | 18.2% |
| cleanup | 1 | 0.0% | 0.0% |
Router step distribution: 1: 566, 2: 395, 3: 18, 5: 120
Router step distribution: 1: 567, 2: 395, 3: 18, 5: 120
Boundaries applied (ADR / границы): 8 of 1099 эпизодов (0.7%).
Boundaries applied (ADR / границы): 8 of 1100 эпизодов (0.7%).
## Активные многоэтапные проекты
@@ -126,7 +126,7 @@ Episodes since last run: 542 / threshold: 10
## Reviewer: субагент vs fallback
0 эпизодов проверено из 1120.
0 эпизодов проверено из 1121.
## Reviewer findings
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env node
/**
* objection-format — дословные сводки возражений для блок-сообщений (П1).
* Чистые форматтеры (презентация, не логика гейта): судья и наставник.
* Fail-safe: мусор/нет возражений → пустая строка.
*/
/** Дословная сводка возражений судьи (форма parseJudgeResponse: objections[{anchor:{ref},severity}]). */
export function formatJudgeObjection(verdict) {
const objs = 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}). */
export function formatMentorObjection(r) {
if (!r || r.wired !== true || r.ok === true) return '';
const head = typeof r.reason === 'string' && r.reason ? `Замечание наставника: ${r.reason}` : 'Замечание наставника.';
const objs = r.verdict && Array.isArray(r.verdict.objections) ? r.verdict.objections : [];
const lines = objs.filter((o) => o && o.anchor && o.anchor.ref).map((o) => `${o.anchor.ref}`);
return [head, ...lines].join('\n');
}
+44
View File
@@ -0,0 +1,44 @@
import { describe, it, expect } from 'vitest';
import { formatJudgeObjection, formatMentorObjection } from './objection-format.mjs';
describe('formatJudgeObjection', () => {
it('склеивает дословные возражения (anchor.ref + severity)', () => {
const v = { objections: [
{ verdict: 'NO', anchor: { kind: 'failed_criterion', ref: 'шаг 3 не проверяемый исход' }, severity: 'heavy' },
], slots: {} };
const s = formatJudgeObjection(v);
expect(s).toContain('шаг 3 не проверяемый исход');
expect(s).toContain('heavy');
});
it('несколько возражений — все в выводе', () => {
const v = { objections: [{ anchor: { ref: 'A' }, severity: 'fatal' }, { anchor: { ref: 'B' }, severity: 'light' }] };
const s = formatJudgeObjection(v);
expect(s).toContain('A');
expect(s).toContain('B');
});
it('нет возражений → пустая строка', () => {
expect(formatJudgeObjection({ objections: [], slots: {} })).toBe('');
});
it('мусорный вердикт не роняет', () => {
expect(formatJudgeObjection(null)).toBe('');
expect(formatJudgeObjection({ objections: 'x' })).toBe('');
});
it('возражение без anchor.ref пропускается', () => {
expect(formatJudgeObjection({ objections: [{ severity: 'heavy' }] })).toBe('');
});
});
describe('formatMentorObjection', () => {
it('собирает дословный reason + замечания вердикта', () => {
const s = formatMentorObjection({ ok: false, wired: true, reason: 'шаг 2 склеен', verdict: { objections: [{ anchor: { ref: 'разбей шаг 2' } }] } });
expect(s).toContain('шаг 2 склеен');
expect(s).toContain('разбей шаг 2');
});
it('ok=true → пустая строка (нет замечания)', () => {
expect(formatMentorObjection({ ok: true, wired: true })).toBe('');
});
it('не wired → пустая строка ($0, наставник не судил)', () => {
expect(formatMentorObjection({ ok: false, wired: false })).toBe('');
});
it('мусор не роняет', () => { expect(formatMentorObjection(null)).toBe(''); });
});