Andrey Epifancev c8b92fae01 docs(dsh): пробный прогон SDD — режимы, дефект и fix-loop (#41)
Три задачи в temp-репозитории: implementer'ы deepseek-flash, все ревью
deepseek-v4-pro. Ревьюер Task 2 независимо нашёл T2.1 (медиана чётной длины
противоречит спеке) и T2.2 (нет теста), fix-раунд 1 → 186575c, скоуп-ре-ревью
APPROVED, финальное ревью ветки APPROVED. Режим 2 (фоновый контроллер, глубина
2) проверен отдельной пробой: вызов subagent из контроллера прошёл.

Артефакты прогона — openspec/changes/dsh-sdd/trial/ (план, спека, брифы,
отчёты, ledger), результаты — design.md. По урокам прогона skill sdd уточнён:
DONE_WITH_CONCERNS при замеченном расхождении, число тестов в плане — ориентир,
повтор при ошибке валидации provider/model.

Co-Authored-By: DeepSeek Harness <noreply@deepseek.com>
2026-10-24 17:49:38 +04:00

227 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Design: DSH-native SDD
## Context
Claude-SDD (`~/.dsh/skills/subagent-driven-development/SKILL.md`) опирается на
синхронный `Task`: контроллер вызвал implementer'а и в том же ходе получил
отчёт. DSH так не умеет — и это не деталь, а другая форма исполнения.
Что измерено в DSH 0.2.1-rc.3 (`@deepseek-ai/dsh-tool-subagent`,
`dsh-subagent`, `dsh-tool-workflow`, `dsh-workflow-ptc`):
| Свойство | Значение в DSH | Следствие для SDD |
|---|---|---|
| `subagent` возвращает | `started subagent <id>` сразу, результат — уведомлением позже | нет синхронного ожидания; ход контроллера заканчивается |
| Продолжение ребёнка | `send_message` (провайдер `spawn` continuable) | раунды фикса идут в того же implementer'а |
| `subagent_fork` | наследует завершённые ходы родителя | для implementer'а не годится: нужен свежий контекст |
| `subagent.maxDepth` | по умолчанию **1** (настройка сервиса, volatile) | контроллер-сабагент не позовёт implementer'а, пока не поднято до 2 |
| Выбор модели у `subagent` | только при `modelSelectionSettings: true` на строке `tool-subagent`; иначе ребёнок наследует модель сессии | «укажи модель в dispatch» невыполнимо без правки профиля |
| `workflow` | синхронный, один вызов инструмента; `agent(prompt, {schema, provider, model, label, phase})` | годится для детерминированного fan-out с выбором модели без настроек |
| Кэпы `workflow` | `maxTotalAgents`, `maxConcurrentAgents`, `maxItemsPerCall` | backstop для веера; для 3 задач не упираемся |
| Наблюдение за детьми | `list_agents`, `interrupt_agent`, уведомления | «дождаться» = закончить ход и проснуться от уведомления |
| Модели в окружении | `deepseek-official/deepseek-flash`, `deepseek-official/deepseek-v4-pro` | три тира роли схлопываются в два |
Строка `subagent-model-selection-settings` есть в web/desktop-профиле и
отсутствует в headless. Поэтому флаг `modelSelectionSettings` нельзя ставить в
бандл: headless упадёт на загрузке с «requires … in the Host scope». Он живёт
в opt-in overlay профиля.
## Goals / Non-Goals
**Goals**
- Инструкция, по которой DSH-сессия исполняет план без вранья о возможностях
платформы: два режима контроллера, протокол отчётов, ревью задачи, fix-loop,
ledger с `## Findings`, финальное ревью.
- Проверяемый прогон: план из 3 задач в temp-репозитории, дефект найден
ревьюером и закрыт в fix-loop.
- Тесты, идущие без DSH runtime.
- Явная таблица отличий от Claude-SDD.
**Non-Goals**
- Паритет с `Task`: его не будет, и это фиксируется, а не имитируется.
- Переписывание `~/.dsh/skills/subagent-driven-development/` — skill
superpowers остаётся как есть; DSH-семантика живёт в отдельном skill.
- Правка `roles/dev.md` в общем слое: роль для DSH — слой `~/.dsh/roles`.
- Автоматическое включение выбора модели и глубины: это решение владельца.
## Решения владельца (brainstorming 24.10.2026)
- **Режим контроллера зависит от контекста, а не один на всех.**
Одна сложная задача — контроллер главная сессия. Несколько задач параллельно,
каждая по SDD, — контроллер фоновый сабагент на задачу.
## Design
### Режим 1 — главная сессия контроллер
Главная сессия читает план, ведёт ledger, диспатчит `subagent` (depth 1,
укладывается в дефолтный `maxDepth: 1`), получает уведомление, диспатчит
ревьюера, при находках — `send_message` implementer'у. Плюс: рулинги по
конфликтам плана принимает тот, у кого есть спека и контекст. Минус: контекст
главной сессии растёт, поэтому отчёты — файлами, а в контекст возвращается
только статус, коммиты и одна строка тестов.
### Режим 2 — фоновый контроллер
Один `subagent` на план, `maxDepth: 2`. Контроллер не блокируется: диспатч
implementer'а возвращает id, ход заканчивается, уведомление о завершении
начинает следующий ход. Пропущенное уведомление ловится `list_agents` перед
следующим диспатчем. Главная сессия в это время свободна для другой задачи.
### Протокол (общий для обоих режимов)
1. Setup: ledger `.superpowers/sdd/<plan>/progress.md`, первая строка —
путь плана; скан плана на конфликты (таблица в ledger, рулинги до Task 1).
2. Dispatch implementer'а: brief файлом, отчёт файлом, контракт — `DONE`,
`DONE_WITH_CONCERNS`, `NEEDS_CONTEXT`, `BLOCKED`.
3. Ревью задачи: пакет диффа файлом, вердикт по спеке и по качеству, находки
`T<n>.<k>`.
4. Fix-loop до 5 раундов: раунды 1–3 — тот же implementer через
`send_message`, 4–5 — свежий и на тир выше; затем адъюдикация.
5. Финальное ревью ветки: `C-n`/`I-n`/`M-n`, один fix-диспатч, одно
скоуп-ревью.
6. Ledger: `## Findings` с маркером и статусом; рулинги `R-n (design #n)`.
### Модели
Два маршрута вместо трёх тиров:
| Роль в SDD | Маршрут | Почему |
|---|---|---|
| implementer по готовому коду из плана | `deepseek-official/deepseek-flash` | транскрипция + тесты |
| implementer по прозе, интеграция | `deepseek-official/deepseek-v4-pro` | решение в нескольких файлах |
| ревью задачи, ре-ревью | `deepseek-official/deepseek-v4-pro` | вердикт важнее цены |
| финальное ревью ветки | `deepseek-official/deepseek-v4-pro` | самый широкий дифф |
Без overlay всё идёт на модели сессии (`deepseek-flash`); тиры становятся
недоступны, и инструкция это явно говорит, а не молча игнорирует.
### Где живёт
`dsh/skills/sdd/SKILL.md` — в репозитории обвязки, ставится
`dsh/install.sh --skills` в `~/.dsh/skills`. Claude Code его не получает:
DoD порта требует, чтобы CC-харнес не менялся.
## Альтернативы
- **Только workflow.** Детерминированный fan-out силён для независимых задач,
но решения по конфликтам плана, адъюдикация находок и «стоп, план врёт»
требуют суждения в моменте. Скрипт может ветвиться по структурному ответу
ревьюера, но не может решить, что находка неверна.
- **Только фоновый контроллер.** Дороже на одну задачу и требует поднятой
глубины; для одиночной задачи лишний уровень.
- **Гибрид.** `workflow` — для веера независимых задач внутри плана,
контроллер — для последовательной петли. Держим как расширение, в первой
версии не включаем: веер ещё не встречался в планах этого репозитория.
## Риски
- Уведомление о завершении ребёнка не пришло (обрыв, компакция) — контроллер
обязан сверяться с `list_agents` и git, а не с памятью.
- `maxDepth: 2` не поднят, а режим 2 выбран — диспатч упадёт
`subagent depth 2 exceeds maxDepth 1`; инструкция требует проверить до
старта и откатиться в режим 1 с записью рулинга.
- Ledger лежит в git-ignored scratch: `git clean -fdx` его уничтожает;
восстановление — по `git log`.
- Модели две: «haiku» из Claude-текста не существует, перенос формулировок
тиров без таблицы превратится в фикцию.
## Что должен доказать пробный прогон
| Пункт приёмки #41 | Чем доказывается |
|---|---|
| 3 задачи не теряются | ledger: три `Task N: complete`, три коммита |
| отчёты собираются | файлы отчётов implementer'ов + строки в ledger |
| ревьюер находит дефект | `T1.1` с `fixed <commit>` |
| fix-loop завершается | раунд в ledger, ре-ревью чистое |
| ledger содержит `## Findings` со статусами | сам файл |
| отличие от Claude-SDD указано | таблица выше + раздел в skill |
## Результаты пробного прогона 24.10.2026
Площадка — `/tmp/dsh-sdd-trial`: отдельный git-репозиторий, ветка `feat/trial`,
база `main`; модуль `src/stats.py` из трёх задач. Контроллер — главная сессия
(Режим 1), overlay профиля включён (`model selection` + `maxDepth: 2`),
`deepseek-flash` — implementer'ы, `deepseek-v4-pro` — все ревью. Артефакты
скопированы в `trial/`: `SPEC.md`, `plan.md`, брифы, отчёты implementer'ов и
ревьюеров, `ledger.md` (первая строка — путь плана, `## Findings`, `## Rulings`).
### Диспатчи
| Шаг | Агент | Маршрут | Результат |
|---|---|---|---|
| Task 1 implementer | `1c906f6f` | flash | `DONE`, `dc1670f`, 2 теста OK |
| Task 1 review | `68a1aeb4` | v4-pro | `APPROVED`, находок нет |
| Task 2 implementer | `1ec8f7fc` | flash | `DONE`, `836a730`, 4 теста OK, concern: план противоречит спеке на чётной длине |
| Task 2 review | `e50c74d2` | v4-pro | `CHANGES_REQUESTED`: `T2.1`, `T2.2` (проверено независимо от concerns) |
| Task 2 fix, раунд 1 | `1ec8f7fc` (send_message) | flash | `186575c`, 6 тестов OK |
| Task 2 re-review (скоуп) | `7912c16b` | v4-pro | `APPROVED`: `T2.1 fixed 186575c`, `T2.2 fixed 186575c` |
| Task 3 implementer | `6ca07edc` | flash | `DONE`, `7cb0383`, 7 тестов OK |
| Task 3 review | `f10e3165` | v4-pro | `APPROVED`, находок нет |
| Final branch review | `0fec8102` | v4-pro | `APPROVED`, находок нет |
### Приёмка #41
| Пункт приёмки | Чем доказан | Статус |
|---|---|---|
| 3 задачи не теряются | ledger `Task 1..3: complete`, 4 коммита `dc1670f`/`836a730`/`186575c`/`7cb0383` | выполнен |
| отчёты собираются | 9 файлов в `trial/reports/`, статус первой строкой у каждого | выполнен |
| ревьюер находит и проверяет дефект | `T2.1` найден независимо (`median([1,2]) == 2.0` вместо `1.5`), `T2.2` — отсутствие теста; оба `fixed 186575c`, ре-ревью APPROVED | выполнен |
| fix-loop завершается | раунд 1 из 5, ре-ревью чистое | выполнен |
| ledger содержит `## Findings` со статусами | `trial/ledger.md` | выполнен |
| итоговый прогон зелёный | `python3 -m unittest discover -s tests` → 7 тестов OK; `bash dsh/tests/run.sh` и `bash tests/run.sh` зелёные | выполнен |
| отличие от Claude-SDD указано | таблица в `dsh/skills/sdd/SKILL.md` | выполнен |
### Находки и наблюдения прогона
| Маркер | Что | Статус |
|---|---|---|
| `T2.1` | `median` на чётной длине возвращал верхний средний элемент вместо среднего двух | `fixed 186575c` |
| `T2.2` | не было теста на чётную длину | `fixed 186575c` |
| `O-1` | «Interfaces» плана обещают аннотации типов, «Exact code» их не содержит; SPEC аннотаций не требует | `rejected` (правка плана в прогоне не даёт результата) |
| `O-2`, `O-3` | первый вызов `subagent` с переданными `provider`/`model` вернул «child LLM provider and model must be supplied together»; немедленный повтор тем же вызовом проходит (2 раза за прогон) | `deferred` (наблюдение о стабильности валидации DSH; повтор в skill) |
| `O-4` | ожидаемое число тестов в плане (`5`) устарело после fix-раунда Task 2 (фактически 7) | `fixed` — в skill добавлено правило «число тестов в плане — ориентир» |
| `O-5` | тест `describe([1,2,3])` на симметричном входе не отличил бы перестановку `mean`/`median` | `deferred` (реализация верна, слабость плана) |
Отдельно: implementer Task 2 заметил расхождение плана со спекой, но поставил
`DONE`, а не `DONE_WITH_CONCERNS`; ревью дефект всё равно поймало, потому что
ревьюер проверял по `SPEC.md`, а не по отчёту. Правило уточнено в skill:
замеченное расхождение с брифом — это `DONE_WITH_CONCERNS`.
### Режим 2 (фоновый контроллер)
Отдельная площадка `/tmp/dsh-sdd-mode2`. Контроллер `afe886e5` (flash) вызвал
`subagent` (flash) и получил id; ход закончился, уведомление о завершении пришло
отдельным сообщением, после чего контроллер проверил отчёт, коммит `0601d6e` и
тесты независимо. Глубина 2 работает с overlay; синхронного ожидания нет.
### Отличия от дизайна, обнаруженные прогоном
- Число тестов в плане — ориентир: fix-раунд добавляет тесты, и расхождение
вверх не дефект (O-4).
- Ошибка валидации `provider`/`model` нестабильна; лечится повтором, а не
пересмотром маршрутов (O-2/O-3).
- Ревьюер, проверяющий по спеке, а не по отчёту implementer'а, ловит дефекты,
даже если implementer промолчал бы.
## Decisions made without discussion
- **24.10.2026 — overlay вместо флага в бандле.** Обоснование: headless-профиль
не содержит строку `subagent-model-selection-settings`, включённый флаг роняет
загрузку. Цена ошибки: владельцу нужно скопировать три записи в профиль
руками; забыл — работает на модели сессии.
- **24.10.2026 — карта тиров.** `flash` — транскрипция и механика, `v4-pro` —
интеграция и все ревью. Цена ошибки: дороже прогон или слабее ревью; правится
одной таблицей в skill.
- **24.10.2026 — skill ставится только в `~/.dsh`.** Обоснование: DoD порта
запрещает менять CC-харнес. Цена ошибки: в Claude-сессиях инструкции про
DSH-режимы нет — там работает исходный `subagent-driven-development`.
- **24.10.2026 — гибрид отложен.** `workflow` описан как расширение, не
реализуется в этом change. Цена ошибки: веер задач придётся ввести позже
отдельной правкой skill.
## Открытые вопросы
- Нужно ли после приёмки добавить в `~/.dsh/roles/dev.md` строку «в DSH
исполнение плана — skill `sdd`» (сейчас роль ссылается на superpowers-SDD).