# Наставник: доводы контроллера + переоценка (Фикс 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: подать наставнику доводы контроллера (его позицию по кругам) и обязать переоценивать
текущий план — снятые претензии не повторять.

```skills-json
["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`

```javascript
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`

```javascript
#!/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`

```javascript
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:

```javascript
    'Вынеси РЕШЕНИЕ: decision="GO" если можно реализовывать, decision="NO-GO" если нужна переделка (тогда в recommendation — ЧТО править).',
```

new:

```javascript
    'Переоцени ТЕКУЩИЙ план/спеку: сначала проверь, сняты ли твои ПРОШЛЫЕ претензии из журнала переговоров; снятое НЕ повторяй; учти доводы контроллера; новое возражение — только если оно реально в текущем тексте.',
    'Вынеси РЕШЕНИЕ: decision="GO" если можно реализовывать, decision="NO-GO" если нужна переделка (тогда в recommendation — ЧТО править).',
```

- [ ] **Step 4: Edit `tools/mentor-verdict.mjs`** — в `buildMentorSpecVerdictPrompt`, вставить ПЕРЕД «Вынеси РЕШЕНИЕ» (спека-вариант):

old:

```javascript
    'Вынеси РЕШЕНИЕ: decision="GO" если спеку можно принять, decision="NO-GO" если нужна переделка (тогда в recommendation — ЧТО править).',
```

new:

```javascript
    'Переоцени ТЕКУЩИЙ план/спеку: сначала проверь, сняты ли твои ПРОШЛЫЕ претензии из журнала переговоров; снятое НЕ повторяй; учти доводы контроллера; новое возражение — только если оно реально в текущем тексте.',
    'Вынеси РЕШЕНИЕ: 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:

```javascript
  const negotiationLog = (journal0.entries || [])
    .map((e) => e && e.payload)
    .filter((p) => p && p.task_id === taskIdForPrompt);
```

new:

```javascript
  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

```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"}
]
```

```verified-context-json
[
  {"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

(пусто)
