9.2 KiB
Наставник: доводы контроллера + переоценка (Фикс 2b) — Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: superpowers:executing-plans (inline).
Goal: Наставник видит доводы контроллера из «Переговоров» и обязан переоценить текущий план, не повторяя снятые претензии (чинит F-C).
Architecture: Извлечение записей контроллера из «Переговоров» → в negotiationLog наставника + инструкция переоценки в промптах наставника.
Tech Stack: Node.js ESM, vitest.
Цель
Закрыть F-C: подать наставнику доводы контроллера (его позицию по кругам) и обязать переоценивать текущий план — снятые претензии не повторять.
["test-driven-development"]
File Structure
- Create:
tools/controller-negotiation.mjs(+.test.mjs) — записи контроллера для журнала. - Create:
tools/mentor-reeval.test.mjs— тест инструкции переоценки. - Modify:
tools/mentor-verdict.mjs— инструкция переоценки в обоих промптах наставника. - Modify:
tools/enforce-mentor-on-plan-write.mjs— добавить записи контроллера в negotiationLog (план-путь).
Task 1: Записи контроллера из «Переговоров»
- Step 1: Тест —
tools/controller-negotiation.test.mjs
import { describe, it, expect } from 'vitest';
import { controllerNegotiationEntries } from './controller-negotiation.mjs';
describe('controllerNegotiationEntries', () => {
it('пустой/нет раздела → []', () => {
expect(controllerNegotiationEntries('')).toEqual([]);
expect(controllerNegotiationEntries('# План\nтекст')).toEqual([]);
});
it('извлекает круги контроллера дословно', () => {
const md = '## Переговоры\n\n### Круг 1\nкарточка добавлена в тело плана\n';
expect(controllerNegotiationEntries(md)).toEqual([
{ round: 1, side: 'controller', utterance: 'карточка добавлена в тело плана', justification: '' },
]);
});
});
-
Step 2: vitest RED —
npx vitest run --root app --config vitest.config.tools.mjs controller-negotiation→ FAIL. -
Step 3: Модуль —
tools/controller-negotiation.mjs
#!/usr/bin/env node
/** controller-negotiation — доводы контроллера из «Переговоров» плана для журнала наставника (Фикс 2b). */
import { parseNegotiationSection } from './negotiation-section.mjs';
export function controllerNegotiationEntries(planContent) {
const rounds = parseNegotiationSection(String(planContent ?? ''));
return rounds.map((r) => ({ round: r.round, side: 'controller', utterance: r.position, justification: '' }));
}
- Step 4: vitest GREEN —
npx vitest run --root app --config vitest.config.tools.mjs controller-negotiation→ PASS (2).
Task 2: Инструкция переоценки в промптах наставника
- Step 1: Тест —
tools/mentor-reeval.test.mjs
import { describe, it, expect } from 'vitest';
import { buildMentorVerdictPrompt, buildMentorSpecVerdictPrompt } from './mentor-verdict.mjs';
describe('mentor re-eval instruction', () => {
it('verdict-промпт: system содержит инструкцию переоценки', () => {
const { system } = buildMentorVerdictPrompt({ plan: { steps: [] } });
expect(system).toContain('Переоцени ТЕКУЩИЙ');
expect(system).toContain('снятое НЕ повторяй');
});
it('spec-промпт: system содержит инструкцию переоценки', () => {
const { system } = buildMentorSpecVerdictPrompt({ specContent: 'x' });
expect(system).toContain('Переоцени ТЕКУЩИЙ');
});
});
-
Step 2: vitest RED —
npx vitest run --root app --config vitest.config.tools.mjs mentor-reeval→ FAIL. -
Step 3: Edit
tools/mentor-verdict.mjs— вbuildMentorVerdictPrompt, вставить строку ПЕРЕД «Вынеси РЕШЕНИЕ» (план-вариант):
old:
'Вынеси РЕШЕНИЕ: decision="GO" если можно реализовывать, decision="NO-GO" если нужна переделка (тогда в recommendation — ЧТО править).',
new:
'Переоцени ТЕКУЩИЙ план/спеку: сначала проверь, сняты ли твои ПРОШЛЫЕ претензии из журнала переговоров; снятое НЕ повторяй; учти доводы контроллера; новое возражение — только если оно реально в текущем тексте.',
'Вынеси РЕШЕНИЕ: decision="GO" если можно реализовывать, decision="NO-GO" если нужна переделка (тогда в recommendation — ЧТО править).',
- Step 4: Edit
tools/mentor-verdict.mjs— вbuildMentorSpecVerdictPrompt, вставить ПЕРЕД «Вынеси РЕШЕНИЕ» (спека-вариант):
old:
'Вынеси РЕШЕНИЕ: decision="GO" если спеку можно принять, decision="NO-GO" если нужна переделка (тогда в recommendation — ЧТО править).',
new:
'Переоцени ТЕКУЩИЙ план/спеку: сначала проверь, сняты ли твои ПРОШЛЫЕ претензии из журнала переговоров; снятое НЕ повторяй; учти доводы контроллера; новое возражение — только если оно реально в текущем тексте.',
'Вынеси РЕШЕНИЕ: decision="GO" если спеку можно принять, decision="NO-GO" если нужна переделка (тогда в recommendation — ЧТО править).',
- Step 5: vitest GREEN —
npx vitest run --root app --config vitest.config.tools.mjs mentor-reeval→ PASS (2).
Task 3: Проводка — доводы контроллера в negotiationLog (план-путь)
- Step 1: Edit
tools/enforce-mentor-on-plan-write.mjs— после построенияnegotiationLog(план-путь) добавить записи контроллера:
old:
const negotiationLog = (journal0.entries || [])
.map((e) => e && e.payload)
.filter((p) => p && p.task_id === taskIdForPrompt);
new:
const negotiationLog = (journal0.entries || [])
.map((e) => e && e.payload)
.filter((p) => p && p.task_id === taskIdForPrompt);
// Фикс 2b: добавить доводы контроллера из «Переговоров» плана в журнал, который видит наставник.
try {
const { controllerNegotiationEntries } = await import('./controller-negotiation.mjs');
negotiationLog.push(...controllerNegotiationEntries(content));
} catch { /* best-effort: доводы контроллера не ломают круг */ }
(Спека-путь — симметричный follow-up для brain; здесь покрыт план-путь как основной канал переговоров.)
- Step 2: Полная регрессия —
npx vitest run --root app --config vitest.config.tools.mjs→ PASS (0 регрессий).
steps-json
[
{"op":"Write","object":"tools/controller-negotiation.test.mjs","ref":"D1"},
{"op":"Bash","object":"npx vitest run --root app --config vitest.config.tools.mjs controller-negotiation","ref":"D5"},
{"op":"Write","object":"tools/controller-negotiation.mjs","ref":"D1"},
{"op":"Bash","object":"npx vitest run --root app --config vitest.config.tools.mjs controller-negotiation","ref":"D5"},
{"op":"Write","object":"tools/mentor-reeval.test.mjs","ref":"D2"},
{"op":"Bash","object":"npx vitest run --root app --config vitest.config.tools.mjs mentor-reeval","ref":"D5"},
{"op":"Edit","object":"tools/mentor-verdict.mjs","ref":"D2"},
{"op":"Edit","object":"tools/mentor-verdict.mjs","ref":"D2"},
{"op":"Bash","object":"npx vitest run --root app --config vitest.config.tools.mjs mentor-reeval","ref":"D5"},
{"op":"Edit","object":"tools/enforce-mentor-on-plan-write.mjs","ref":"D3"},
{"op":"Bash","object":"npx vitest run --root app --config vitest.config.tools.mjs","ref":"D5"}
]
[
{"id":"D1-parse","kind":"EXTRACTED","ref":"tools/negotiation-section.mjs","anchor":"export function parseNegotiationSection("},
{"id":"D2-prompt","kind":"EXTRACTED","ref":"tools/mentor-verdict.mjs","anchor":"export function buildMentorVerdictPrompt("}
]
Переговоры
Круг 1
(пусто)