Files
portal/docs/superpowers/plans/2026-06-16-mentor-reeval.md
T

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 REDnpx 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 GREENnpx 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 REDnpx 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 GREENnpx 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

(пусто)