From ee0d876f6b8def2be99937d097f2bff169684d7f Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 24 Oct 2026 16:59:53 +0400 Subject: [PATCH 1/4] =?UTF-8?q?docs(dsh):=20=D0=B4=D0=B8=D0=B7=D0=B0=D0=B9?= =?UTF-8?q?=D0=BD=20SDD=20=D0=B4=D0=BB=D1=8F=20DSH=20=E2=80=94=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B6=D0=B8=D0=BC=D1=8B=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80?= =?UTF-8?q?=D0=BE=D0=BB=D0=BB=D0=B5=D1=80=D0=B0=20=D0=B8=20=D0=BC=D0=B0?= =?UTF-8?q?=D1=80=D1=88=D1=80=D1=83=D1=82=D1=8B=20=D0=BC=D0=BE=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B5=D0=B9=20(#41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Design note и spec delta для фазы #41: два режима контроллера (главная сессия / фоновый сабагент), асинхронный протокол диспатча и отчётов, fix-loop, ledger с ## Findings, карта из двух маршрутов моделей. Основание — измеренные ограничения DSH 0.2.1-rc.3: subagent.maxDepth по умолчанию 1, у subagent нет provider/model без modelSelectionSettings, выбор модели работает сразу только в workflow.agent(). Co-Authored-By: DeepSeek Harness --- openspec/changes/dsh-sdd/.openspec.yaml | 2 + openspec/changes/dsh-sdd/design.md | 159 ++++++++++++++++++ openspec/changes/dsh-sdd/proposal.md | 71 ++++++++ .../changes/dsh-sdd/specs/dsh-sdd/spec.md | 105 ++++++++++++ 4 files changed, 337 insertions(+) create mode 100644 openspec/changes/dsh-sdd/.openspec.yaml create mode 100644 openspec/changes/dsh-sdd/design.md create mode 100644 openspec/changes/dsh-sdd/proposal.md create mode 100644 openspec/changes/dsh-sdd/specs/dsh-sdd/spec.md diff --git a/openspec/changes/dsh-sdd/.openspec.yaml b/openspec/changes/dsh-sdd/.openspec.yaml new file mode 100644 index 0000000..1f5d280 --- /dev/null +++ b/openspec/changes/dsh-sdd/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-24 diff --git a/openspec/changes/dsh-sdd/design.md b/openspec/changes/dsh-sdd/design.md new file mode 100644 index 0000000..df3b47a --- /dev/null +++ b/openspec/changes/dsh-sdd/design.md @@ -0,0 +1,159 @@ +# 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 ` сразу, результат — уведомлением позже | нет синхронного ожидания; ход контроллера заканчивается | +| Продолжение ребёнка | `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//progress.md`, первая строка — + путь плана; скан плана на конфликты (таблица в ledger, рулинги до Task 1). +2. Dispatch implementer'а: brief файлом, отчёт файлом, контракт — `DONE`, + `DONE_WITH_CONCERNS`, `NEEDS_CONTEXT`, `BLOCKED`. +3. Ревью задачи: пакет диффа файлом, вердикт по спеке и по качеству, находки + `T.`. +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 ` | +| fix-loop завершается | раунд в ledger, ре-ревью чистое | +| ledger содержит `## Findings` со статусами | сам файл | +| отличие от Claude-SDD указано | таблица выше + раздел в skill | + +## 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). diff --git a/openspec/changes/dsh-sdd/proposal.md b/openspec/changes/dsh-sdd/proposal.md new file mode 100644 index 0000000..4dc55f6 --- /dev/null +++ b/openspec/changes/dsh-sdd/proposal.md @@ -0,0 +1,71 @@ +# Proposal: DSH-native SDD + +Issue: #41 (роадмап порта — #48). Источники: +- `dsh/PORT.md`, разделы 3 (deferred: SDD), 7 (риски), 8 (next phases, п. 4); +- brainstorming 24.10.2026 (ответы владельца о режимах контроллера); +- разбор исходников DSH 0.2.1-rc.3: `dsh-tool-subagent`, `dsh-tool-workflow`, + `dsh-subagent`, `dsh-workflow-ptc`. + +## Why + +SDD — единственное место, где DSH расходится с Claude Code не текстом, а +семантикой. В Claude Code контроллер исполняет план, вызывая `Task` и получая +результат в том же ходе; в DSH `subagent` возвращает id и завершается +уведомлением позже, а `workflow` исполняет детерминированный скрипт и ждёт +всех детей. Пока эта разница не переписана, `subagent-driven-development` +в DSH-сессии либо врёт о возможностях платформы, либо ломается на первом +ожидании. + +Дополнительно разбор исходников показал две вещи, которые меняют план: + +1. Глубина делегирования по умолчанию — 1. Фоновый контроллер-сабагент не + может позвать implementer'а, пока `subagent.maxDepth` не поднят до 2. +2. У инструмента `subagent` нет `provider`/`model` в схеме, пока строке + `tool-subagent` не поставлен `modelSelectionSettings: true`; ребёнок + наследует модель сессии. Выбор модели работает сразу только внутри + `workflow.agent()`. Маршрутов в этом окружении два: + `deepseek-official/deepseek-flash` и `deepseek-official/deepseek-v4-pro`. + +Без этих двух фактов любая инструкция SDD «укажи модель в dispatch» невыполнима +и превращается в фикцию. + +## What Changes + +- **Новый DSH-skill `dsh/skills/sdd/SKILL.md`** — инструкция исполнения плана + в DSH: два режима контроллера (главная сессия и фоновый сабагент), протокол + dispatch/wait/report, ревью задачи, fix-loop, ledger `## Findings`, рулинги, + финальное ревью ветки, отличие от Claude-SDD одной таблицей. + Ставится `dsh/install.sh --skills` в `~/.dsh/skills`; Claude Code его не + получает — харнес Claude не меняется. +- **`dsh/subagent-models.yml`** — opt-in overlay профиля: `modelSelectionSettings` + для `tool-subagent` и `tool-subagent-fork` плюс `subagent.maxDepth: 2`. + В бандл не входит: строка `subagent-model-selection-settings` есть в + web/desktop-профиле и отсутствует в headless, и включённый флаг уронил бы + headless на загрузке. +- **`dsh/PORT.md`** — раздел о включении выбора модели и глубины, ссылка на + overlay, снятие пункта 4 из «Next phases» после приёмки. +- **`dsh/tests/sdd-skill.test.mjs`** — проверки, которые идут без DSH runtime: + frontmatter skill, оба режима, имена инструментов, требование маршрута + модели, отсутствие ссылок на несуществующие средства (`job_output` как + ожидание сабагента, `maxDepth` без оговорки). +- **Пробный прогон** в temp-репозитории: план из 3 задач, implementer → + reviewer → fix → финальное ревью; транскрипт и ledger — в отчёте issue. + Прогон доказывает семантику, а не заменяет тесты: без DSH runtime его не + повторить, поэтому он делается один раз и результаты фиксируются в + `design.md` и в комментарии к #41. + +## Impact + +- Claude-harness не меняется: `roles/`, `hooks/`, `global/`, `install.sh`, + `agents/` остаются как есть. +- Бандл не меняет состав строк профиля; новое поведение — только по явному + решению владельца через overlay. +- Роль dev после приёмки должна ссылаться на новый skill для DSH-сессий + (одна строка; правится в `~/.dsh/roles`, не в общем `roles/dev.md`). + +## Acceptance + +Из #41, без изменений: три задачи не теряются, отчёты собираются, ревьюер +находит и проверяет хотя бы один дефект, fix-loop завершается, ledger содержит +`## Findings` со статусами, прогон тестов зелёный, отличие от Claude-SDD +указано явно. diff --git a/openspec/changes/dsh-sdd/specs/dsh-sdd/spec.md b/openspec/changes/dsh-sdd/specs/dsh-sdd/spec.md new file mode 100644 index 0000000..e7461e4 --- /dev/null +++ b/openspec/changes/dsh-sdd/specs/dsh-sdd/spec.md @@ -0,0 +1,105 @@ +# Spec Delta: dsh-sdd + +## Purpose + +Исполнение плана субагентами в DeepSeek Harness (SDD). Контроллер диспатчит +implementer'а на задачу плана, получает отчёт, отправляет задачу на ревью +и ведёт fix-loop до чистоты, затем заказывает финальное ревью ветки. DSH не +имеет синхронного `Task`, поэтому протокол строится на асинхронном `subagent` +(идентификатор, уведомление, `send_message`) и на ledger как единственной +памяти между ходами. + +## ADDED Requirements + +### Requirement: Режим контроллера выбирается по контексту +Skill SHALL предлагать два режима и MUST выбирать между ними по числу +параллельных планов, а не по умолчанию. + +- Один план в сессии — контроллер главная сессия; implementer'ы и ревьюеры + диспатчатся на глубину 1, что укладывается в `subagent.maxDepth` по + умолчанию. +- Несколько задач, каждая по SDD, — контроллер фоновый сабагент на план, + глубина 2. + +Skill MUST проверить доступную глубину до первого диспатча и MUST записать +рулинг с откатом в режим главной сессии, если глубина меньше нужной. + +#### Scenario: Одна сложная задача +- **WHEN** в сессии один план и он архитектурный +- **THEN** контроллером остаётся главная сессия, а субагенты вызываются с + глубины 1 + +#### Scenario: Глубина не поднята +- **WHEN** выбрано несколько параллельных планов, а `subagent.maxDepth` равен 1 +- **THEN** skill не диспатчит контроллера, фиксирует рулинг об откате в режим + главной сессии и продолжает исполнение в нём + +### Requirement: Асинхронный протокол диспатча и отчёта +Диспатч implementer'а SHALL передавать требования файлом брифа, а результат +MUST возвращаться файлом отчёта; в контекст контроллера возвращается только +статус, коммиты, одна строка сводки тестов и concerns. Контракт статусов: +`DONE`, `DONE_WITH_CONCERNS`, `NEEDS_CONTEXT`, `BLOCKED`. + +#### Scenario: Отчёт файлом +- **WHEN** implementer завершил задачу +- **THEN** полный отчёт лежит в файле отчёта, а в ledger и контекст попали + только статус, коммиты и строка тестов + +### Requirement: Ожидание без блокировки +Skill MUST NOT ждать ребёнка синхронно: в DSH такого средства нет. Пока +у контроллера есть локальная работа, он MUST продолжать её; когда работы нет, +он MUST закончить ход и продолжить по уведомлению о завершении. Перед каждым +следующим диспатчем контроллер MUST сверить живых детей через `list_agents`, +чтобы поймать завершившегося без отчёта. + +#### Scenario: Ребёнок завершился без отчёта +- **WHEN** уведомление о завершении не пришло, а `list_agents` показывает + неактивного ребёнка +- **THEN** контроллер считает задачу завершённой по git-состоянию и ledger, + а не перезапускает её + +### Requirement: Ревью задачи и fix-loop +Каждая задача MUST проходить ревью по спеке и по качеству; implementer self-review +его не заменяет. Fix-loop MUST длиться не более 5 раундов: раунды 1–3 — тот же +implementer через `send_message`, раунды 4–5 — свежий implementer на тир выше. +После пятого раунда контроллер MUST адъюдицировать каждую открытую находку. + +#### Scenario: Дефект найден и закрыт +- **WHEN** ревьюер задачи вернул находку по существу +- **THEN** раунд фикса уходит тому же implementer'у, ре-ревью ограничено + исправлением, находка получает статус `fixed ` + +### Requirement: Ledger и маркировка находок +Своя рабочая директория плана SHALL содержать `progress.md` с первой строкой — +путём плана. Ledger MUST содержать таблицу сканирования плана, рулинги +`R-n (design #n)` и раздел `## Findings`, где у каждой находки есть маркер +источника (`T.`, `C-n`/`I-n`/`M-n`) и статус `fixed `, +`deferred` или `rejected <причина>`. + +#### Scenario: Находка без статуса +- **WHEN** финальное ревью ветки вернуло находку, а исполнение остановлено +- **THEN** отчёт контроллера обязан показать её со статусом; находка без + статуса считается ошибкой контроллера + +### Requirement: Тира моделей и деградация +Skill MUST называть маршруты моделей явно при каждом диспатче, если выбор +модели доступен, и MUST NOT выдавать это за гарантию: у `subagent` параметры +`provider`/`model` появляются только после включения выбора модели в профиле. +Без включённого выбора модели skill MUST исполнять всё на модели сессии и +указывать это в отчёте. + +#### Scenario: Выбор модели выключен +- **WHEN** профиль не включает выбор модели для `subagent` +- **THEN** все implementer'ы и ревьюеры идут на модели сессии, а отчёт + фиксирует отсутствие тиров как наблюдение, а не как выполненное требование + +### Requirement: Отличие от Claude-SDD зафиксировано +Skill SHALL содержать таблицу отличий от `subagent-driven-development` Claude +Code: синхронный `Task` против асинхронного `subagent`, наличие `send_message`, +глубина по умолчанию, выбор модели, отсутствие уведомления как штатного +ожидания. Skill MUST NOT утверждать паритет с Claude-SDD. + +#### Scenario: Чтение skill перед первым диспатчем +- **WHEN** модель загрузила skill и читает таблицу отличий +- **THEN** из неё видно, что ожидание строится на завершении хода и + уведомлении, а не на возврате значения инструмента -- 2.47.2 From 2bee63d305de93696ca7bc00cc65b816dfa4f0c2 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 24 Oct 2026 17:37:54 +0400 Subject: [PATCH 2/4] =?UTF-8?q?feat(dsh):=20overlay=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D1=84=D0=B8=D0=BB=D1=8F=20=E2=80=94=20model=20selection=20?= =?UTF-8?q?=D0=B8=20maxDepth=202=20(#41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dsh/subagent-models.yml — opt-in overlay: subagent-model-selection-settings с маршрутами deepseek-flash/deepseek-v4-pro и subagent.maxDepth: 2. В бандл не входит: строки нет в headless, включённый флаг ломает загрузку. install.sh --check сообщает overlay и находит maxDepth: 2 в патче профиля. PORT.md §5 — как включить и что это даёт; §7/§8 обновлены. Co-Authored-By: DeepSeek Harness --- docs/superpowers/plans/2026-10-24-dsh-sdd.md | 241 +++++++++++++++++++ dsh/PORT.md | 37 ++- dsh/install.sh | 18 ++ dsh/subagent-models.yml | 32 +++ dsh/tests/subagent-models.test.mjs | 39 +++ 5 files changed, 363 insertions(+), 4 deletions(-) create mode 100644 docs/superpowers/plans/2026-10-24-dsh-sdd.md create mode 100644 dsh/subagent-models.yml create mode 100644 dsh/tests/subagent-models.test.mjs diff --git a/docs/superpowers/plans/2026-10-24-dsh-sdd.md b/docs/superpowers/plans/2026-10-24-dsh-sdd.md new file mode 100644 index 0000000..0182687 --- /dev/null +++ b/docs/superpowers/plans/2026-10-24-dsh-sdd.md @@ -0,0 +1,241 @@ +# DSH-native SDD — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Дать DSH-сессии инструкцию исполнения плана субагентами (асинхронный `subagent`, ledger, ревью, fix-loop) и opt-in overlay профиля с глубиной 2 и двумя маршрутами моделей; доказать семантику пробным прогоном из 3 задач. + +**Architecture:** Три слоя. (1) `dsh/subagent-models.yml` — overlay, который владелец копирует в `cordis.patch.yml` профиля: включает `subagent-model-selection-settings` (маршруты `deepseek-flash` / `deepseek-v4-pro`) и `subagent.maxDepth: 2`; в бандл не входит, иначе падает headless. (2) `dsh/skills/sdd/SKILL.md` — DSH-семантика исполнения плана: два режима контроллера, протокол ожидания через завершение хода и уведомление, ревью задачи, fix-loop, ledger `## Findings`. (3) Пробный прогон в temp-репозитории: контроллер — главная сессия, implementer (flash) → reviewer (v4-pro) → fix через `send_message` → финальное ревью (v4-pro); отдельной пробой проверяется режим 2 (фоновый контроллер, глубина 2). + +**Tech Stack:** bash, python3 stdlib, Node test runner (`node --test`), markdown, DSH 0.2.1-rc.3 (Cordis bundle, `subagent`, `send_message`, `list_agents`), Gitea CLI `tea`. + +**Spec:** `openspec/changes/dsh-sdd/` — `proposal.md`, `design.md` (источник решений), `specs/dsh-sdd/spec.md` (требования и сценарии). + +**Ветка:** `feat/41-dsh-sdd-spike`, issue #41, worktree `.claude/worktrees/sdd-spike`. + +## Global Constraints + +- DSH 0.2.1-rc.3; версия плагинного API не заморожена, после апгрейда тесты прогоняются заново. +- Бандл импортирует только Node builtins и локальные файлы (`dsh/tests/package.test.mjs` это проверяет): никаких `@deepseek-ai/*` импортов. +- `subagent-model-selection-settings` нельзя ставить в `dsh/bundle/cordis.patch.yml`: строки нет в headless-профиле, включённый флаг роняет загрузку. Только overlay профиля. +- Skill `sdd` ставится только в `~/.dsh/skills` через `bash dsh/install.sh --skills`. Claude Code его не получает: `roles/`, `hooks/`, `global/`, `install.sh` (корневой), `agents/` не меняются. +- Тесты идут без DSH runtime: `bash dsh/tests/run.sh` и `bash tests/run.sh` (630/630 на момент дизайна) — зелёными должны быть оба набора. +- Документация и тексты — по-русски, формат планов/ledger — как в этом репозитории. +- Коммиты — с trailer `Co-Authored-By: DeepSeek Harness `, force-push запрещён. +- Маркировка: находки ревью `T.` / `C-n` / `I-n` / `M-n`, рулинги `R-n (design #n)`, наблюдения `O-n`, замечания человека `H-n`; у каждой находки статус `fixed ` / `deferred` / `rejected <причина>`. + +## Review Focus + +- Overlay копируют в headless-профиль → загрузка падает на «requires … in the Host scope»; overlay и PORT.md обязаны это сказать явно, а не только в design.md. +- Skill прочитан в сессии без model selection → диспатч с `provider`/`model` молча игнорируется или падает; skill обязан деградировать на модель сессии и записать это наблюдением, а не выдавать за выполненное требование. +- Ожидание описано как `job_output`/поллинг или как возврат значения `subagent` → инструкция врёт о платформе; ожидание — только конец хода + уведомление + `list_agents`. +- Фоновый контроллер при `maxDepth: 1` → `subagent depth 2 exceeds maxDepth 1`; skill обязан проверить глубину до диспатча и откатиться в режим 1 с рулингом. +- Ledger без `## Findings`/статусов или находка без маркера → отчёт контроллера считается ошибочным; тест skill обязан требовать разделы, а пробный прогон — показать заполненный ledger. +- `install.sh --skills` с новым skill ломает счётчик «copied 1 local skills» в `dsh/tests/install.test.mjs` → счётчик и проверка наличия `sdd` обновляются в той же задаче. + +--- + +### Task 1: Overlay профиля и статическая проверка глубины + +**Files:** +- Create: `dsh/subagent-models.yml` +- Create: `dsh/tests/subagent-models.test.mjs` +- Modify: `dsh/install.sh` (`check()`, после проверки hook bridge) +- Modify: `dsh/PORT.md` (§5 Configuration — подраздел про overlay и глубину; §7 — риск «Model tiers»; §8 — п. 4) + +**Interfaces:** +- Produces: файл `dsh/subagent-models.yml` — YAML-массив из двух записей: `- id: subagent-model-selection-settings` с `config.enabled: true` и `config.allowedModels` из двух пар (`deepseek-official/deepseek-flash`, `deepseek-official/deepseek-v4-pro`), затем `- id: subagent` с `config.maxDepth: 2`. +- Produces: строки вывода `check()`: `ok: subagent overlay (<путь>)` или `WARN: subagent overlay missing …`; `ok: subagent maxDepth=2 (<файл>)` или `WARN: subagent maxDepth is not 2 in any profile patch; SDD mode 2 is unavailable`. +- Consumes: ничего из других задач. + +- [ ] **Step 1: Написать падающий тест** `dsh/tests/subagent-models.test.mjs` + +```js +import assert from 'node:assert/strict' +import { spawnSync } from 'node:child_process' +import { existsSync, readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import test from 'node:test' +import { fileURLToPath } from 'node:url' +import { cleanup, tempDir, write } from './helpers.mjs' + +const here = dirname(fileURLToPath(import.meta.url)) +const overlay = join(here, '..', 'subagent-models.yml') +const script = join(here, '..', 'install.sh') + +test('overlay enables model selection for both routes and depth 2', () => { + assert.ok(existsSync(overlay), 'dsh/subagent-models.yml is missing') + const text = readFileSync(overlay, 'utf8') + assert.match(text, /id: subagent-model-selection-settings/) + assert.match(text, /model: deepseek-flash/) + assert.match(text, /model: deepseek-v4-pro/) + assert.match(text, /id: subagent\n\s+config:\n\s+maxDepth: 2/) + assert.match(text, /headless/) +}) + +test('install.sh --check reports the overlay and the profile depth', () => { + const home = tempDir() + try { + const dshHome = join(home, '.dsh') + const env = { ...process.env, HOME: home, DSH_HOME: dshHome } + + const missing = spawnSync('bash', [script, '--check'], { env, encoding: 'utf8' }) + assert.match(missing.stderr, /subagent maxDepth is not 2/) + + write(join(dshHome, 'profiles', 'desktop', 'cordis.patch.yml'), '- id: subagent\n config:\n maxDepth: 2\n') + const present = spawnSync('bash', [script, '--check'], { env, encoding: 'utf8' }) + assert.match(present.stdout, /ok: subagent overlay/) + assert.match(present.stdout, /ok: subagent maxDepth=2/) + } finally { + cleanup(home) + } +}) +``` + +- [ ] **Step 2: Прогнать тест и убедиться, что он падает** + +Run: `node --test dsh/tests/subagent-models.test.mjs` +Expected: FAIL — `dsh/subagent-models.yml is missing`. + +- [ ] **Step 3: Создать `dsh/subagent-models.yml`** + +YAML-массив с комментарием-шапкой: overlay для DSH-native SDD (#41); копировать в `$DSH_HOME/profiles/desktop/cordis.patch.yml`, не в бандл, потому что строки `subagent-model-selection-settings` нет в headless; после копирования — рестарт DSH; проверка — `bash dsh/install.sh --check`. Две записи из блока Interfaces, значения — дословно. + +- [ ] **Step 4: Добавить проверки в `dsh/install.sh`** + +В `check()` после блока hook bridge: проверить наличие `$ROOT/dsh/subagent-models.yml`; отдельно — `grep -qs 'maxDepth: 2' "$DSH_HOME"/profiles/*/cordis.patch.yml` (glob не раскрылся — предупреждение, не ошибка; `failed` не поднимать: overlay опционален). + +- [ ] **Step 5: Обновить `dsh/PORT.md`** + +§5: подраздел «Model routes and delegation depth» — как включить (скопировать `dsh/subagent-models.yml` в профиль, рестарт), что даёт (provider/model у `subagent` и `subagent_fork`, глубина 2), чего не делает (не в бандле; без overlay всё идёт на модели сессии). +§7: в пункте «Model tiers» — два маршрута и ссылка на overlay. +§8: п. 4 (SDD spike) получает статус «в работе, change `dsh-sdd`». + +- [ ] **Step 6: Прогнать тесты** + +Run: `node --test dsh/tests/subagent-models.test.mjs` +Expected: PASS (2 tests). + +- [ ] **Step 7: Прогнать оба набора** + +Run: `bash dsh/tests/run.sh && bash tests/run.sh` +Expected: все зелёные (если в worktree падает guard на 5 тестах — проверить `ls -ld dsh/bundle/hooks`, должен быть симлинк `../../hooks`, O-1). + +- [ ] **Step 8: Коммит** + +```bash +git add dsh/subagent-models.yml dsh/tests/subagent-models.test.mjs dsh/install.sh dsh/PORT.md +git commit -m "feat(dsh): overlay профиля — model selection и maxDepth 2 (#41)" +``` + +--- + +### Task 2: Skill `sdd` — DSH-семантика исполнения плана + +**Files:** +- Create: `dsh/skills/sdd/SKILL.md` +- Create: `dsh/tests/sdd-skill.test.mjs` +- Modify: `dsh/tests/install.test.mjs` (ожидание «copied 2 local skills», проверка `skills/sdd/SKILL.md`) +- Modify: `dsh/README.md` (What works / Not yet / Layout / Known limits) + +**Interfaces:** +- Produces: skill с frontmatter `name: sdd` и `description: Use when executing an implementation plan in DeepSeek Harness …`; текст содержит: оба режима, `maxDepth`, инструменты `subagent`/`send_message`/`list_agents`/`interrupt_agent`, статусы `DONE`/`DONE_WITH_CONCERNS`/`NEEDS_CONTEXT`/`BLOCKED`, маршруты `deepseek-official/deepseek-flash` и `deepseek-official/deepseek-v4-pro`, ledger с `## Findings`, таблицу отличий от Claude-SDD. +- Consumes: `dsh/subagent-models.yml` из Task 1 (ссылка в разделе про глубину и модели). + +- [ ] **Step 1: Написать падающий тест** `dsh/tests/sdd-skill.test.mjs` + +Проверки (каждая — отдельный `test`): +1. frontmatter: `/^---\nname: sdd\n/`, `description: .+`; +2. режимы: `/Режим 1/`, `/Режим 2/`, `/maxDepth: 2/` и `/глубин/`; +3. инструменты: `subagent`, `send_message`, `list_agents`, `interrupt_agent`; +4. статусы: `DONE_WITH_CONCERNS`, `NEEDS_CONTEXT`, `BLOCKED`; +5. маршруты: `deepseek-official/deepseek-flash`, `deepseek-official/deepseek-v4-pro`; +6. ledger: `## Findings`, `fixed `, `T.`; +7. ожидание: `/закончи ход/` и `/уведомлен/`; НЕ содержит `job_output`; +8. отличие: `синхронный` и `асинхронный`, `subagent-driven-development`; +9. деградация: `/модель сессии/`, `/не паритет/` — заголовок таблицы отличий с запретом выдавать за паритет; +10. размер: `Buffer.byteLength(text) < 12288`. + +Run: `node --test dsh/tests/sdd-skill.test.mjs` → FAIL (файла нет). + +- [ ] **Step 2: Написать `dsh/skills/sdd/SKILL.md`** + +Разделы: When to use / when not (DSH-сессии; в Claude Code — исходный `subagent-driven-development`); два режима и правило выбора (один план — главная сессия, глубина 1; несколько параллельных планов — фоновый контроллер на план, глубина 2) + проверка глубины до первого диспатча и рулинг об откате; протокол диспатча (бриф файлом, отчёт файлом, контракт статусов, в контекст — только статус, коммиты, одна строка тестов); ожидание (конец хода, уведомление, `list_agents` перед каждым диспатчем, `interrupt_agent` для зависших, запрет `job_output`); ревью задачи (спека + качество) и fix-loop 5 раундов (1–3 — тот же implementer через `send_message`, 4–5 — свежий на тир выше, потом адъюдикация); финальное ревью ветки `C-n`/`I-n`/`M-n`; ledger (первая строка — путь плана, таблица конфликтов, рулинги `R-n (design #n)`, `## Findings` со статусами); маршруты моделей и деградация; таблица отличий от `subagent-driven-development` (синхронный `Task` vs асинхронный `subagent`, `send_message`, глубина по умолчанию, выбор модели, ожидание); запреты (не заявлять паритет, `workflow` — расширение, в первой версии не включать). + +- [ ] **Step 3: Прогнать тест** + +Run: `node --test dsh/tests/sdd-skill.test.mjs` → PASS. + +- [ ] **Step 4: Обновить `dsh/tests/install.test.mjs`** + +`copied 1 local skills` → `copied 2 local skills`; после проверки `role` проверить `join(dshHome, 'skills', 'sdd', 'SKILL.md')` — существует и содержит `name: sdd`. + +- [ ] **Step 5: Обновить `dsh/README.md`** + +What works: SDD-инструкция и overlay. Not yet: убрать SDD, оставить `briefing`, `harness-check`, `role-hint`, `harness-doctor`, auto-memory, caveman. Layout: `skills/sdd/SKILL.md`, `subagent-models.yml`. Known limits: заменить строку про `sonnet`/`haiku`/`opus` ссылкой на overlay. + +- [ ] **Step 6: Прогнать всё** + +Run: `bash dsh/tests/run.sh && bash tests/run.sh` → зелёные. + +- [ ] **Step 7: Коммит** + +```bash +git add dsh/skills/sdd/SKILL.md dsh/tests/sdd-skill.test.mjs dsh/tests/install.test.mjs dsh/README.md +git commit -m "feat(dsh): skill sdd — асинхронный протокол исполнения плана (#41)" +``` + +--- + +### Task 3: Пробный прогон 3 задач и запись результатов + +**Files:** +- Create: `/tmp/dsh-sdd-trial*` — temp-репозиторий (не входит в PR) +- Modify: `openspec/changes/dsh-sdd/design.md` — раздел «Результаты пробного прогона» +- Modify: `dsh/PORT.md` §8 — п. 4 закрыт ссылкой на прогон +- Modify: этот план — отметить шаги +- Comment: issue #41, затем PR (`tea pulls create`) + +**Interfaces:** +- Consumes: skill `sdd` из Task 2, overlay из Task 1 (в профиле уже включён владельцем). +- Produces: транскрипт прогона (диспатчи, статусы, отчёты, ledger) в `design.md`; вывод о режимах 1 и 2. + +- [ ] **Step 1: Создать temp-репозиторий и план** + +`TRIAL="$(mktemp -d)/trial"`; `git init -b main`, коммит `README.md`; `git checkout -b feat/trial`; файл `docs/superpowers/plans/2026-10-24-trial.md` — один план (require-plan разрешает правки), проект: Python-модуль `src/stats.py`. Три задачи: (1) `mean(values)` — float, `ValueError` на пустом списке, готовый код и тест в плане; (2) `median(values)` — по требованиям для чётной длины среднее двух средних; **в плане намеренно оставлен дефект**: `return xs[len(xs) // 2]`, а тест в плане покрывает только нечётную длину (дефект должен найти ревьюер, не тесты); (3) `describe(values)` — dict `{count, mean, median}` через две функции. Тесты — `python3 -m unittest discover -s tests`. Спека прогона — `SPEC.md` в temp-репо: требования к `mean`/`median`/`describe`, включая «чётная длина — среднее двух средних». + +- [ ] **Step 2: Ledger прогона** + +`.superpowers/sdd/trial-plan/progress.md`: первая строка — путь плана; таблица сканирования плана (конфликт: план Task 2 противоречит `SPEC.md` — рулинг `R-1`: следовать `SPEC.md`, дефект фиксировать находкой `T2.1` при ревью, а не править план до диспатча); пустой `## Findings`; три строки `Task N: pending`. + +- [ ] **Step 3: Диспатч implementer'а Task 1 (режим 1)** + +`subagent` c `description` «trial task 1», `cwd` = temp-репо, `provider: deepseek-official`, `model: deepseek-flash`; промпт — brief файлом `briefs/task-1.md` (контекст, требования, точный код, тест, коммит), отчёт — `reports/task-1.md` с первой строкой-статусом; требование: `python3 -m unittest discover -s tests` зелёный, коммит. После вызова — закончить ход; продолжить по уведомлению. + +- [ ] **Step 4: Собрать отчёт Task 1 и отправить на ревью** + +Прочитать `reports/task-1.md`, сверить коммит (`git -C "$TRIAL" log --oneline`) и прогон тестов; `list_agents` — сверка состояния; в ledger `Task 1: complete`. Диффпакет — `review/pkg-task-1.txt` (`git diff main...HEAD`); `subagent` с `model: deepseek-v4-pro`: ревью по `SPEC.md` и по качеству, находки `T1.`, вердикт в `reports/review-1.md`. + +- [ ] **Step 5: Task 2 — диспатч, ревью, fix-loop** + +Так же диспатчить Task 2 (flash, brief с кодом из плана и дефектом). Ревью (v4-pro) должно вернуть `T2.1` — чётная длина медианы. Fix-раунд 1: `send_message` тому же implementer'у с находкой; по уведомлению — тест на чётную длину, фикс, коммит; ре-ревью скопом — новый `subagent` (v4-pro) с пакетом `review/pkg-fix-1.txt`; в ledger `T2.1: fixed `. Если ревьюер дефект не нашёл — зафиксировать `O-1` (протокол пройден, но приёмка #41 «ревьюер находит дефект» не доказана) и повторить ревью с явным указанием проверять требование «чётная длина». + +- [ ] **Step 6: Task 3 и финальное ревью ветки** + +Task 3 — flash, по плану; ревью — v4-pro; финальное ревью всей ветки — `subagent` v4-pro по полному диффу `main...HEAD`, находки `C-n`/`I-n`/`M-n`; один fix-диспатч при находках, одно скоуп-ревью. + +- [ ] **Step 7: Проба режима 2 (глубина 2)** + +Отдельный temp-репозиторий или новая ветка в том же: `subagent` (flash) с промптом «ты контроллер, задиспатчь implementer'а на задачу X, дождись уведомления, верни отчёт»; контроллер обязан сам вызвать `subagent` и завершиться. Записать: работает / падает `exceeds maxDepth` / уведомление не пришло. При падении — находка `O-n` и вывод, что режим 2 требует поднятой глубины (overlay уже в профиле). + +- [ ] **Step 8: Записать результаты** + +`openspec/changes/dsh-sdd/design.md` — раздел «Результаты пробного прогона 24.10.2026»: таблица приёмки #41 (пункт → чем доказан → статус), реальные диспатчи с моделями, найденный дефект `T2.1` с коммитом фикса, ledger дословно, вывод по режимам, наблюдения `O-n` со статусами. `dsh/PORT.md` §8 п. 4 — закрыт. + +- [ ] **Step 9: Проверки, коммит, PR** + +Run: `bash dsh/tests/run.sh && bash tests/run.sh`; `openspec validate dsh-sdd --strict` (если поддерживается). +Коммит `docs(dsh): пробный прогон SDD — режимы, дефект и fix-loop (#41)`, push, `tea pulls create --base main --head feat/41-dsh-sdd-spike --title "DSH: SDD — skill, overlay, пробный прогон (#41)"`. Комментарий в #41 — состояние фазы и ссылка на PR. + +## Acceptance + +Из #41: три задачи не теряются (ledger `Task 1..3: complete` + три коммита в temp-репо), отчёты собираются (файлы отчётов + строки ledger), ревьюер находит и проверяет хотя бы один дефект (`T2.1` `fixed `), fix-loop завершается (ре-ревью чистое), ledger содержит `## Findings` со статусами, прогон тестов зелёный, отличие от Claude-SDD указано таблицей в skill. diff --git a/dsh/PORT.md b/dsh/PORT.md index 6783fe8..354fa66 100644 --- a/dsh/PORT.md +++ b/dsh/PORT.md @@ -162,6 +162,32 @@ for the repository layout: bootstrapMaxBytes: 65536 ``` +### Model routes and delegation depth + +SDD needs two things the default profile does not give: a model choice for +`subagent` and a second level of delegation for a background controller. +`dsh/subagent-models.yml` is the opt-in overlay for both: + +```bash +# 1. copy the two entries into the profile patch (desktop is Electron-managed) +open -e ~/.dsh/profiles/desktop/cordis.patch.yml # append dsh/subagent-models.yml +# 2. restart DSH, then check +bash dsh/install.sh --check # "ok: subagent maxDepth=2 (...)" +``` + +After the restart the `subagent` and `subagent_fork` schemas accept +`provider`, `model`, `reasoning_effort`, and a background controller subagent +can dispatch an implementer. Routes measured in 0.2.1-rc.3: +`deepseek-official/deepseek-flash` (transcription and mechanics) and +`deepseek-official/deepseek-v4-pro` (integration and every review). Without the +overlay the agent runs everything on the session model and at depth 1; the +`sdd` skill must say so in its report instead of pretending the routes exist. + +The overlay stays out of `dsh/bundle/cordis.patch.yml` on purpose: the +`subagent-model-selection-settings` row exists in the web/desktop profile and is +absent in headless, and enabling it from the bundle would break headless boot +with "requires ... in the Host scope". + `bootstrapSkillPath` resolution order: explicit path, `DSH_SUPERPOWERS_SKILL`, `~/.dsh/skills`, `~/.agents/skills`, the newest `~/.claude/plugins/cache/claude-plugins-official/superpowers/*/`, then @@ -271,8 +297,9 @@ profile was removed; the real `desktop` profile was not touched. - **Subagent bootstrap.** `isSubagent` probes common fields; if none are present on this version, the bootstrap reaches children too. The skill's `` makes that harmless but token-expensive. -- **Model tiers.** `sonnet`/`haiku`/`opus` are not DSH models. Map them to live - provider/model routes before porting the SDD text. +- **Model tiers.** `sonnet`/`haiku`/`opus` are not DSH models. The port uses two + live routes (`deepseek-flash`, `deepseek-v4-pro`) from the opt-in overlay in + `dsh/subagent-models.yml`; without it every dispatch runs on the session model. - **`~/.claude` dependence.** `dsh/install.sh --skills --roles` copies skills and roles into `~/.dsh`; the bootstrap and `/role` prefer those copies, and the Claude cache/roles are fallbacks. An existing installation must rerun @@ -291,8 +318,10 @@ profile was removed; the real `desktop` profile was not touched. 2. Add `harness-check` and optional `role-hint` as non-blocking pre-step injections. 3. Build `briefing` as an agent preset (read-only tools + model route). -4. SDD spike: async controller (`subagent` + `send_message`, `maxDepth: 2`) for - one plan, or a `workflow`-based executor; measure which fits the harness. +4. SDD: change `dsh-sdd` — the `sdd` skill (async controller, reports, review, + fix-loop, ledger) plus the overlay in §5; proven by a three-task trial run in + a temporary repository. See + `openspec/changes/dsh-sdd/design.md`. 5. Enable CodeGraph MCP by default when the binary is present; update role and project instructions to `mcp__codegraph__codegraph_explore`. 6. Port `harness-doctor` checks to DSH paths. diff --git a/dsh/install.sh b/dsh/install.sh index fdcebd6..8ce501d 100755 --- a/dsh/install.sh +++ b/dsh/install.sh @@ -102,6 +102,24 @@ check() { echo "FAIL: hook bridge is missing (is dsh/bundle/hooks a symlink to ../../hooks?)" >&2 failed=1 fi + if [ -f "$ROOT/dsh/subagent-models.yml" ]; then + echo "ok: subagent overlay ($ROOT/dsh/subagent-models.yml)" + else + echo "WARN: subagent overlay missing; SDD runs on the session model at depth 1" >&2 + fi + local patch found=0 + for patch in "$DSH_HOME"/profiles/*/cordis.patch.yml; do + [ -f "$patch" ] || continue + if grep -qs 'maxDepth: 2' "$patch"; then + found=1 + break + fi + done + if [ "$found" = 1 ]; then + echo "ok: subagent maxDepth=2 ($patch)" + else + echo "WARN: subagent maxDepth is not 2 in any profile patch; SDD mode 2 is unavailable" >&2 + fi if [ -f "$DSH_HOME/skills/using-superpowers/SKILL.md" ]; then echo "ok: dsh skills ($DSH_HOME/skills)" elif superpowers_skills >/dev/null 2>&1; then diff --git a/dsh/subagent-models.yml b/dsh/subagent-models.yml new file mode 100644 index 0000000..25f820a --- /dev/null +++ b/dsh/subagent-models.yml @@ -0,0 +1,32 @@ +# Opt-in overlay for DSH-native SDD (issue #41). +# +# Copy these two entries into $DSH_HOME/profiles/desktop/cordis.patch.yml, then +# restart DSH (the plugin page must reload the profile). Verify with: +# bash dsh/install.sh --check +# # in a new session the `subagent` tool schema shows provider/model/reasoning_effort +# +# Do NOT add them to dsh/bundle/cordis.patch.yml: the +# `subagent-model-selection-settings` row exists in the web/desktop profile and +# is absent in headless, so enabling it in the bundle breaks headless boot +# ("requires ... in the Host scope"). +# +# What it unlocks: +# - `subagent` and `subagent_fork` accept provider/model/reasoning_effort, +# so an implementer can run on a cheaper route than the reviewer; +# - `subagent.maxDepth: 2`, so a background controller subagent (depth 1) +# can dispatch implementers (depth 2). +# +# Routes measured in DSH 0.2.1-rc.3: +# deepseek-official/deepseek-flash - transcription and mechanics +# deepseek-official/deepseek-v4-pro - integration and every review +- id: subagent-model-selection-settings + config: + enabled: true + allowedModels: + - provider: deepseek-official + model: deepseek-flash + - provider: deepseek-official + model: deepseek-v4-pro +- id: subagent + config: + maxDepth: 2 diff --git a/dsh/tests/subagent-models.test.mjs b/dsh/tests/subagent-models.test.mjs new file mode 100644 index 0000000..55eb21f --- /dev/null +++ b/dsh/tests/subagent-models.test.mjs @@ -0,0 +1,39 @@ +import assert from 'node:assert/strict' +import { spawnSync } from 'node:child_process' +import { existsSync, readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import test from 'node:test' +import { fileURLToPath } from 'node:url' +import { cleanup, tempDir, write } from './helpers.mjs' + +const here = dirname(fileURLToPath(import.meta.url)) +const overlay = join(here, '..', 'subagent-models.yml') +const script = join(here, '..', 'install.sh') + +test('overlay enables model selection for both routes and depth 2', () => { + assert.ok(existsSync(overlay), 'dsh/subagent-models.yml is missing') + const text = readFileSync(overlay, 'utf8') + assert.match(text, /id: subagent-model-selection-settings/) + assert.match(text, /model: deepseek-flash/) + assert.match(text, /model: deepseek-v4-pro/) + assert.match(text, /id: subagent\n\s+config:\n\s+maxDepth: 2/) + assert.match(text, /headless/) +}) + +test('install.sh --check reports the overlay and the profile depth', () => { + const home = tempDir() + try { + const dshHome = join(home, '.dsh') + const env = { ...process.env, HOME: home, DSH_HOME: dshHome } + + const missing = spawnSync('bash', [script, '--check'], { env, encoding: 'utf8' }) + assert.match(missing.stderr, /subagent maxDepth is not 2/) + + write(join(dshHome, 'profiles', 'desktop', 'cordis.patch.yml'), '- id: subagent\n config:\n maxDepth: 2\n') + const present = spawnSync('bash', [script, '--check'], { env, encoding: 'utf8' }) + assert.match(present.stdout, /ok: subagent overlay/) + assert.match(present.stdout, /ok: subagent maxDepth=2/) + } finally { + cleanup(home) + } +}) -- 2.47.2 From f714426b11612e0df59011d62f3a2e3e591694d2 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 24 Oct 2026 17:40:24 +0400 Subject: [PATCH 3/4] =?UTF-8?q?feat(dsh):=20skill=20sdd=20=E2=80=94=20?= =?UTF-8?q?=D0=B0=D1=81=D0=B8=D0=BD=D1=85=D1=80=D0=BE=D0=BD=D0=BD=D1=8B?= =?UTF-8?q?=D0=B9=20=D0=BF=D1=80=D0=BE=D1=82=D0=BE=D0=BA=D0=BE=D0=BB=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BB=D0=B0=D0=BD=D0=B0=20(#41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dsh/skills/sdd/SKILL.md: два режима контроллера (главная сессия / фоновый субагент), проверка глубины и маршрутов моделей, диспатч и отчёт файлами, контракт DONE/DONE_WITH_CONCERNS/NEEDS_CONTEXT/BLOCKED, ожидание через завершение хода и уведомление, ревью задачи и fix-loop 5 раундов, ledger с ## Findings, таблица отличий от Claude-SDD. Тесты: frontmatter, режимы, инструменты, маршруты, ledger, отсутствие job_output как ожидания; install.test.mjs ждёт два локальных skill. Co-Authored-By: DeepSeek Harness --- dsh/README.md | 22 ++++-- dsh/skills/sdd/SKILL.md | 142 +++++++++++++++++++++++++++++++++++ dsh/tests/install.test.mjs | 6 +- dsh/tests/sdd-skill.test.mjs | 64 ++++++++++++++++ 4 files changed, 226 insertions(+), 8 deletions(-) create mode 100644 dsh/skills/sdd/SKILL.md create mode 100644 dsh/tests/sdd-skill.test.mjs diff --git a/dsh/README.md b/dsh/README.md index 6373bff..a24d3dc 100644 --- a/dsh/README.md +++ b/dsh/README.md @@ -14,11 +14,15 @@ separate Cordis bundle plus a thin install helper. - roles — the model loads them with the `role` skill (`~/.dsh/roles`, then `~/.claude/roles`, then repo `roles/`); `/role dev|admin|analyst|qa` remains the user-facing command and injects the same text, without Claude's - auto-memory step. + auto-memory step; +- SDD — the `sdd` skill executes a plan through asynchronous `subagent` + dispatches (report files, task review, fix loop, ledger), with controller + modes for one plan and for several parallel plans; the optional overlay + enables model routes and depth 2. -**Not yet:** SDD (DSH subagents are asynchronous), `briefing`, -`harness-check`, `role-hint`, `harness-doctor`, auto-memory, caveman. See -[PORT.md](PORT.md) for the design, mapping table, and risks. +**Not yet:** `briefing`, `harness-check`, `role-hint`, `harness-doctor`, +auto-memory, caveman. See [PORT.md](PORT.md) for the design, mapping table, and +risks. ## Quick start @@ -91,6 +95,8 @@ dsh/ icon.svg tests/ install.sh + skills/{role,sdd}/SKILL.md model-invocable skills installed into ~/.dsh + subagent-models.yml opt-in overlay: model routes + maxDepth 2 mcp.codegraph.yml optional CodeGraph MCP overlay PORT.md design, mapping, risks, next phases ``` @@ -131,6 +137,8 @@ repository layout: `agent/pre-step` injection. If the model skips the skill, use the `/role` command or ask it to load the role explicitly. - Model names in roles and superpowers (`sonnet`, `haiku`, `opus`) are not DSH - models. Map them to live provider/model routes before porting SDD. -- SDD is intentionally out of scope for this spike; see - [PORT.md](PORT.md#3-what-is-deliberately-deferred). + models. The `sdd` skill uses two live routes from the opt-in + `dsh/subagent-models.yml` overlay; without it every dispatch runs on the + session model. +- SDD on DSH is asynchronous, not a copy of Claude's `Task`. See the `sdd` + skill and `openspec/changes/dsh-sdd/`. diff --git a/dsh/skills/sdd/SKILL.md b/dsh/skills/sdd/SKILL.md new file mode 100644 index 0000000..fc9a99f --- /dev/null +++ b/dsh/skills/sdd/SKILL.md @@ -0,0 +1,142 @@ +--- +name: sdd +description: Use when executing an implementation plan in DeepSeek Harness (DSH) with subagents — async dispatch, controller modes, ledger, task review, fix loop, final branch review. Not for Claude Code, where subagent-driven-development applies. +--- + +# SDD в DeepSeek Harness + +Исполнение плана субагентами в DSH. Заменяет `subagent-driven-development` +только в DSH-сессиях: у Claude Code синхронный `Task`, у DSH — асинхронный +`subagent`. Это **не паритет** с Claude-SDD и не имитация: протокол построен +на том, что DSH умеет (уведомление о завершении, `send_message`, ledger), а не +на том, чего у него нет (возврат результата из вызова инструмента). + +## Когда применять + +- Есть план в `docs/superpowers/plans/`, задачи независимы, сессия — DSH. +- Не применять: Claude Code (там исходный `subagent-driven-development`); + плана нет (сначала `brainstorming` → `writing-plans`); правка одна + (дешевле `executing-plans`). + +## Режимы контроллера + +| Условие | Контроллер | Глубина | Когда | +|---|---|---|---| +| Один план в сессии | главная сессия | 1 | по умолчанию; рулинги по конфликтам плана делает тот, у кого спека | +| Несколько задач, каждая по SDD, параллельно | фоновый субагент на план | 2 | когда главная сессия нужна другой задаче | + +**Правило выбора:** режим зависит от числа параллельных планов, а не от +привычки. Один план — Режим 1; несколько — Режим 2 на каждый план. + +**Проверка до первого диспатча.** В Режиме 2 поднята ли глубина: +`bash dsh/install.sh --check` должен сказать `ok: subagent maxDepth=2 (...)`, +или первый диспатч контроллера падает с `exceeds maxDepth`. Если глубины нет — +не диспатчить контроллера, зафиксировать рулинг `R-n` об **откате** в Режим 1 и +продолжить в нём. Проверка выбора модели: в схеме `subagent` есть +`provider`/`model` (overlay `dsh/subagent-models.yml`); нет — все диспатчи идут +на модели сессии, и это наблюдение `O-n`, а не выполненное требование. Без +overlay ребёнок наследует модель сессии. + +## Протокол диспатча и отчёта + +1. Первым делом — ledger (см. ниже) и скан плана на конфликты; рулинги до + Task 1. +2. Бриф implementer'а — **файлом** `/briefs/task-.md`: контекст, + требования задачи, точные имена и сигнатуры, тест, команда проверки, + формат коммита. Промпт короткий: «прочитай бриф, выполни, напиши отчёт, + первая строка отчёта — статус». +3. Отчёт — **файлом** `reports/task-.md`. Контракт первой строки: + `DONE`, `DONE_WITH_CONCERNS`, `NEEDS_CONTEXT`, `BLOCKED`. Дальше: коммиты, + одна строка сводки тестов, concerns, что осталось. +4. В контекст контроллера возвращается только: статус, коммиты, одна строка + тестов и concerns. Полный отчёт читается с диска при ревью, а не тащится + в контекст. +5. `NEEDS_CONTEXT` — ответить в тот же `send_message`; `BLOCKED` — рулинг + контроллера, а не остановка сессии. + +## Ожидание без блокировки + +`subagent` возвращает id и сразу завершается; результата в значении +инструмента нет. Поэтому: + +- вызвал `subagent` — **закончи ход**. Следующий ход начнётся с уведомления о + завершении ребёнка; +- пока ребёнок работает и есть локальная работа (ревью-пакет, ledger, + следующий бриф) — делай её, а не жди; +- перед каждым следующим диспатчем — `list_agents`: неактивный ребёнок без + отчёта означает, что уведомление потерялось; истина — файл отчёта и git, а + не перезапуск задачи; +- ребёнок висит дольше разумного — `interrupt_agent`, затем решение по + состоянию git; +- `job_output` — не ожидание сабагента, а стрим фонового job'а: здесь + неприменим. + +## Ревью задачи и fix-loop + +Каждая задача проходит ревью **свежим** субагентом: соответствие спеке и +качество кода. Self-review implementer'а ревью не заменяет. + +1. Пакет ревью — файлом: дифф задачи, отчёт implementer'а, выдержка из спеки. +2. Ревьюер возвращает вердикт и находки `T.` со статусом. +3. Fix-loop — не более 5 раундов: раунды 1–3 — `send_message` тому же + implementer'у, раунды 4–5 — свежий implementer на более дорогом маршруте. +4. Ре-ревью — только исправление (скоуп), не вся задача. +5. После 5-го раунда — адъюдикация каждой открытой находки: рулинг + `R-n (design #n)` и статус `fixed`/`deferred`/`rejected <причина>`. + +Находка без статуса в отчёте контроллера — ошибка контроллера. + +## Финальное ревью ветки + +После всех задач — один ревьюер на весь дифф `main...HEAD`: находки `C-n` +(critical), `I-n` (important), `M-n` (minor). При находках — **один** +fix-диспатч и одно скоуп-ревью; остатки адъюдицируются и уходят в +`deferred`/follow-up. + +## Ledger + +`.superpowers/sdd//progress.md` в рабочем каталоге плана; первая строка — +путь плана. Обязательные секции: + +- таблица сканирования плана: конфликт, решение, рулинг `R-n (design #n)`; +- `Task : pending | complete` с коммитом; +- `## Findings` — каждая находка: маркер, одна строка, статус + `fixed `, `deferred` или `rejected <причина>`; +- `## Rulings` — рулинги с ценой ошибки. + +Ledger — единственная память между ходами: чат и контекст теряются, а +ledger лежит в git-ignored scratch (после `git clean -fdx` восстанавливается +по `git log`). Поэтому записи не сжимать. + +## Маршруты моделей + +| Роль в SDD | Маршрут | Почему | +|---|---|---| +| implementer по готовому коду из плана | `deepseek-official/deepseek-flash` | транскрипция и тесты | +| implementer по прозе, интеграция | `deepseek-official/deepseek-v4-pro` | решение в нескольких файлах | +| ревью задачи, ре-ревью | `deepseek-official/deepseek-v4-pro` | вердикт важнее цены | +| финальное ревью ветки | `deepseek-official/deepseek-v4-pro` | самый широкий дифф | + +Маршрут указывается в каждом диспатче: `provider`, `model`, +`reasoning_effort`. Параметры действуют только при включённом overlay +(`dsh/subagent-models.yml`); без него всё идёт на модели сессии, и отчёт +фиксирует это наблюдением `O-n`. + +## Отличие от Claude-SDD + +| Свойство | Claude Code `subagent-driven-development` | DSH `sdd` | +|---|---|---| +| Вызов исполнителя | синхронный `Task`, результат в том же ходе | асинхронный `subagent`, id + уведомление позже | +| Ожидание | возврат значения инструмента | закончи ход, продолжи по уведомлению | +| Продолжение ребёнка | новый `Task` | `send_message` в того же ребёнка | +| Глубина по умолчанию | не ограничена | `maxDepth: 2` только с overlay | +| Выбор модели | тиры sonnet/haiku/opus | два маршрута, только с overlay | +| Изоляция контекста | наследование не по умолчанию | `subagent_fork` наследует ходы — для implementer'а не годится | + +## Запреты + +- Не ждать сабагента через `job_output`, поллинг или повторные `list_agents`. +- Не делать merge и force-push; финиш — `finishing-a-development-branch` и PR. +- Не выдавать прогон без overlay за работу на разных моделях. +- `workflow` — расширение для веера независимых задач, в этой версии не + используется: решения по конфликтам и адъюдикация требуют суждения в моменте. diff --git a/dsh/tests/install.test.mjs b/dsh/tests/install.test.mjs index 1a1e819..3cb08d9 100644 --- a/dsh/tests/install.test.mjs +++ b/dsh/tests/install.test.mjs @@ -32,7 +32,7 @@ test('install.sh copies skills and roles instead of linking into ~/.claude', () const first = run() assert.equal(first.status, 0, first.stderr) assert.match(first.stdout, /copied 2 superpowers skills/) - assert.match(first.stdout, /copied 1 local skills/) + assert.match(first.stdout, /copied 2 local skills/) assert.match(first.stdout, /installed 4 roles/) const skill = join(dshHome, 'skills', 'using-superpowers') @@ -44,6 +44,10 @@ test('install.sh copies skills and roles instead of linking into ~/.claude', () assert.ok(existsSync(roleSkill), 'role skill was not installed') assert.match(readFileSync(roleSkill, 'utf8'), /name: role/) + const sddSkill = join(dshHome, 'skills', 'sdd', 'SKILL.md') + assert.ok(existsSync(sddSkill), 'sdd skill was not installed') + assert.match(readFileSync(sddSkill, 'utf8'), /name: sdd/) + for (const role of ['dev', 'admin', 'analyst', 'qa']) { assert.ok(existsSync(join(dshHome, 'roles', `${role}.md`)), `missing ${role}.md`) } diff --git a/dsh/tests/sdd-skill.test.mjs b/dsh/tests/sdd-skill.test.mjs new file mode 100644 index 0000000..3a5c318 --- /dev/null +++ b/dsh/tests/sdd-skill.test.mjs @@ -0,0 +1,64 @@ +import assert from 'node:assert/strict' +import { readFileSync } from 'node:fs' +import { dirname, join } from 'node:path' +import test from 'node:test' +import { fileURLToPath } from 'node:url' + +const here = dirname(fileURLToPath(import.meta.url)) +const skillPath = join(here, '..', 'skills', 'sdd', 'SKILL.md') +const text = () => readFileSync(skillPath, 'utf8') + +test('sdd skill is model-invocable', () => { + const body = text() + assert.match(body, /^---\nname: sdd\n/) + assert.match(body, /\ndescription: .+/) + assert.ok(Buffer.byteLength(body) < 12288, 'skill is over the 12 KiB budget') +}) + +test('sdd skill describes both controller modes and the depth check', () => { + const body = text() + assert.match(body, /Режим 1/) + assert.match(body, /Режим 2/) + assert.match(body, /maxDepth: 2/) + assert.match(body, /глубин/) + assert.match(body, /откат/i) +}) + +test('sdd skill names the asynchronous tools and never job_output', () => { + const body = text() + for (const tool of ['subagent', 'send_message', 'list_agents', 'interrupt_agent']) { + assert.ok(body.includes(tool), `missing ${tool}`) + } + for (const line of body.split('\n').filter((l) => l.includes('job_output'))) { + assert.match(line, /не /i, `job_output proposed as a wait mechanism: ${line}`) + } + assert.match(body, /закончи ход/) + assert.match(body, /уведомлен/) +}) + +test('sdd skill fixes the report contract and the model routes', () => { + const body = text() + for (const status of ['DONE_WITH_CONCERNS', 'NEEDS_CONTEXT', 'BLOCKED']) { + assert.ok(body.includes(status), `missing status ${status}`) + } + assert.match(body, /deepseek-official\/deepseek-flash/) + assert.match(body, /deepseek-official\/deepseek-v4-pro/) + assert.match(body, /модель сессии/) +}) + +test('sdd skill requires the ledger with findings and statuses', () => { + const body = text() + assert.match(body, /## Findings/) + assert.match(body, /fixed /) + assert.match(body, /T\./) + assert.match(body, /deferred/) + assert.match(body, /rejected <причина>/) +}) + +test('sdd skill states the difference from Claude SDD without claiming parity', () => { + const body = text() + assert.match(body, /синхронн/i) + assert.match(body, /асинхронн/i) + assert.match(body, /subagent-driven-development/) + assert.match(body, /не паритет/i) +}) -- 2.47.2 From c8b92fae01a89adbf2808b4b367365d4330db810 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sat, 24 Oct 2026 17:49:38 +0400 Subject: [PATCH 4/4] =?UTF-8?q?docs(dsh):=20=D0=BF=D1=80=D0=BE=D0=B1=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=BF=D1=80=D0=BE=D0=B3=D0=BE=D0=BD=20SDD=20?= =?UTF-8?q?=E2=80=94=20=D1=80=D0=B5=D0=B6=D0=B8=D0=BC=D1=8B,=20=D0=B4?= =?UTF-8?q?=D0=B5=D1=84=D0=B5=D0=BA=D1=82=20=D0=B8=20fix-loop=20(#41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Три задачи в 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 --- docs/superpowers/plans/2026-10-24-dsh-sdd.md | 48 +++---- dsh/PORT.md | 26 ++-- dsh/skills/sdd/SKILL.md | 10 +- openspec/changes/dsh-sdd/design.md | 67 ++++++++++ openspec/changes/dsh-sdd/trial/README.md | 45 +++++++ openspec/changes/dsh-sdd/trial/SPEC.md | 20 +++ .../changes/dsh-sdd/trial/briefs/review-1.md | 14 ++ .../changes/dsh-sdd/trial/briefs/review-2.md | 14 ++ .../changes/dsh-sdd/trial/briefs/review-3.md | 11 ++ .../changes/dsh-sdd/trial/briefs/task-1.md | 15 +++ .../changes/dsh-sdd/trial/briefs/task-2.md | 17 +++ .../changes/dsh-sdd/trial/briefs/task-3.md | 12 ++ openspec/changes/dsh-sdd/trial/ledger.md | 77 +++++++++++ openspec/changes/dsh-sdd/trial/plan.md | 125 ++++++++++++++++++ .../dsh-sdd/trial/reports/fix-2-round-1.md | 31 +++++ .../dsh-sdd/trial/reports/re-review-2.md | 4 + .../changes/dsh-sdd/trial/reports/review-1.md | 31 +++++ .../changes/dsh-sdd/trial/reports/review-2.md | 35 +++++ .../changes/dsh-sdd/trial/reports/review-3.md | 54 ++++++++ .../dsh-sdd/trial/reports/review-final.md | 46 +++++++ .../changes/dsh-sdd/trial/reports/task-1.md | 20 +++ .../changes/dsh-sdd/trial/reports/task-2.md | 29 ++++ .../changes/dsh-sdd/trial/reports/task-3.md | 33 +++++ 23 files changed, 746 insertions(+), 38 deletions(-) create mode 100644 openspec/changes/dsh-sdd/trial/README.md create mode 100644 openspec/changes/dsh-sdd/trial/SPEC.md create mode 100644 openspec/changes/dsh-sdd/trial/briefs/review-1.md create mode 100644 openspec/changes/dsh-sdd/trial/briefs/review-2.md create mode 100644 openspec/changes/dsh-sdd/trial/briefs/review-3.md create mode 100644 openspec/changes/dsh-sdd/trial/briefs/task-1.md create mode 100644 openspec/changes/dsh-sdd/trial/briefs/task-2.md create mode 100644 openspec/changes/dsh-sdd/trial/briefs/task-3.md create mode 100644 openspec/changes/dsh-sdd/trial/ledger.md create mode 100644 openspec/changes/dsh-sdd/trial/plan.md create mode 100644 openspec/changes/dsh-sdd/trial/reports/fix-2-round-1.md create mode 100644 openspec/changes/dsh-sdd/trial/reports/re-review-2.md create mode 100644 openspec/changes/dsh-sdd/trial/reports/review-1.md create mode 100644 openspec/changes/dsh-sdd/trial/reports/review-2.md create mode 100644 openspec/changes/dsh-sdd/trial/reports/review-3.md create mode 100644 openspec/changes/dsh-sdd/trial/reports/review-final.md create mode 100644 openspec/changes/dsh-sdd/trial/reports/task-1.md create mode 100644 openspec/changes/dsh-sdd/trial/reports/task-2.md create mode 100644 openspec/changes/dsh-sdd/trial/reports/task-3.md diff --git a/docs/superpowers/plans/2026-10-24-dsh-sdd.md b/docs/superpowers/plans/2026-10-24-dsh-sdd.md index 0182687..c7197df 100644 --- a/docs/superpowers/plans/2026-10-24-dsh-sdd.md +++ b/docs/superpowers/plans/2026-10-24-dsh-sdd.md @@ -47,7 +47,7 @@ - Produces: строки вывода `check()`: `ok: subagent overlay (<путь>)` или `WARN: subagent overlay missing …`; `ok: subagent maxDepth=2 (<файл>)` или `WARN: subagent maxDepth is not 2 in any profile patch; SDD mode 2 is unavailable`. - Consumes: ничего из других задач. -- [ ] **Step 1: Написать падающий тест** `dsh/tests/subagent-models.test.mjs` +- [x] **Step 1: Написать падающий тест** `dsh/tests/subagent-models.test.mjs` ```js import assert from 'node:assert/strict' @@ -91,36 +91,36 @@ test('install.sh --check reports the overlay and the profile depth', () => { }) ``` -- [ ] **Step 2: Прогнать тест и убедиться, что он падает** +- [x] **Step 2: Прогнать тест и убедиться, что он падает** Run: `node --test dsh/tests/subagent-models.test.mjs` Expected: FAIL — `dsh/subagent-models.yml is missing`. -- [ ] **Step 3: Создать `dsh/subagent-models.yml`** +- [x] **Step 3: Создать `dsh/subagent-models.yml`** YAML-массив с комментарием-шапкой: overlay для DSH-native SDD (#41); копировать в `$DSH_HOME/profiles/desktop/cordis.patch.yml`, не в бандл, потому что строки `subagent-model-selection-settings` нет в headless; после копирования — рестарт DSH; проверка — `bash dsh/install.sh --check`. Две записи из блока Interfaces, значения — дословно. -- [ ] **Step 4: Добавить проверки в `dsh/install.sh`** +- [x] **Step 4: Добавить проверки в `dsh/install.sh`** В `check()` после блока hook bridge: проверить наличие `$ROOT/dsh/subagent-models.yml`; отдельно — `grep -qs 'maxDepth: 2' "$DSH_HOME"/profiles/*/cordis.patch.yml` (glob не раскрылся — предупреждение, не ошибка; `failed` не поднимать: overlay опционален). -- [ ] **Step 5: Обновить `dsh/PORT.md`** +- [x] **Step 5: Обновить `dsh/PORT.md`** §5: подраздел «Model routes and delegation depth» — как включить (скопировать `dsh/subagent-models.yml` в профиль, рестарт), что даёт (provider/model у `subagent` и `subagent_fork`, глубина 2), чего не делает (не в бандле; без overlay всё идёт на модели сессии). §7: в пункте «Model tiers» — два маршрута и ссылка на overlay. §8: п. 4 (SDD spike) получает статус «в работе, change `dsh-sdd`». -- [ ] **Step 6: Прогнать тесты** +- [x] **Step 6: Прогнать тесты** Run: `node --test dsh/tests/subagent-models.test.mjs` Expected: PASS (2 tests). -- [ ] **Step 7: Прогнать оба набора** +- [x] **Step 7: Прогнать оба набора** Run: `bash dsh/tests/run.sh && bash tests/run.sh` Expected: все зелёные (если в worktree падает guard на 5 тестах — проверить `ls -ld dsh/bundle/hooks`, должен быть симлинк `../../hooks`, O-1). -- [ ] **Step 8: Коммит** +- [x] **Step 8: Коммит** ```bash git add dsh/subagent-models.yml dsh/tests/subagent-models.test.mjs dsh/install.sh dsh/PORT.md @@ -141,7 +141,7 @@ git commit -m "feat(dsh): overlay профиля — model selection и maxDepth - Produces: skill с frontmatter `name: sdd` и `description: Use when executing an implementation plan in DeepSeek Harness …`; текст содержит: оба режима, `maxDepth`, инструменты `subagent`/`send_message`/`list_agents`/`interrupt_agent`, статусы `DONE`/`DONE_WITH_CONCERNS`/`NEEDS_CONTEXT`/`BLOCKED`, маршруты `deepseek-official/deepseek-flash` и `deepseek-official/deepseek-v4-pro`, ledger с `## Findings`, таблицу отличий от Claude-SDD. - Consumes: `dsh/subagent-models.yml` из Task 1 (ссылка в разделе про глубину и модели). -- [ ] **Step 1: Написать падающий тест** `dsh/tests/sdd-skill.test.mjs` +- [x] **Step 1: Написать падающий тест** `dsh/tests/sdd-skill.test.mjs` Проверки (каждая — отдельный `test`): 1. frontmatter: `/^---\nname: sdd\n/`, `description: .+`; @@ -157,27 +157,27 @@ git commit -m "feat(dsh): overlay профиля — model selection и maxDepth Run: `node --test dsh/tests/sdd-skill.test.mjs` → FAIL (файла нет). -- [ ] **Step 2: Написать `dsh/skills/sdd/SKILL.md`** +- [x] **Step 2: Написать `dsh/skills/sdd/SKILL.md`** Разделы: When to use / when not (DSH-сессии; в Claude Code — исходный `subagent-driven-development`); два режима и правило выбора (один план — главная сессия, глубина 1; несколько параллельных планов — фоновый контроллер на план, глубина 2) + проверка глубины до первого диспатча и рулинг об откате; протокол диспатча (бриф файлом, отчёт файлом, контракт статусов, в контекст — только статус, коммиты, одна строка тестов); ожидание (конец хода, уведомление, `list_agents` перед каждым диспатчем, `interrupt_agent` для зависших, запрет `job_output`); ревью задачи (спека + качество) и fix-loop 5 раундов (1–3 — тот же implementer через `send_message`, 4–5 — свежий на тир выше, потом адъюдикация); финальное ревью ветки `C-n`/`I-n`/`M-n`; ledger (первая строка — путь плана, таблица конфликтов, рулинги `R-n (design #n)`, `## Findings` со статусами); маршруты моделей и деградация; таблица отличий от `subagent-driven-development` (синхронный `Task` vs асинхронный `subagent`, `send_message`, глубина по умолчанию, выбор модели, ожидание); запреты (не заявлять паритет, `workflow` — расширение, в первой версии не включать). -- [ ] **Step 3: Прогнать тест** +- [x] **Step 3: Прогнать тест** Run: `node --test dsh/tests/sdd-skill.test.mjs` → PASS. -- [ ] **Step 4: Обновить `dsh/tests/install.test.mjs`** +- [x] **Step 4: Обновить `dsh/tests/install.test.mjs`** `copied 1 local skills` → `copied 2 local skills`; после проверки `role` проверить `join(dshHome, 'skills', 'sdd', 'SKILL.md')` — существует и содержит `name: sdd`. -- [ ] **Step 5: Обновить `dsh/README.md`** +- [x] **Step 5: Обновить `dsh/README.md`** What works: SDD-инструкция и overlay. Not yet: убрать SDD, оставить `briefing`, `harness-check`, `role-hint`, `harness-doctor`, auto-memory, caveman. Layout: `skills/sdd/SKILL.md`, `subagent-models.yml`. Known limits: заменить строку про `sonnet`/`haiku`/`opus` ссылкой на overlay. -- [ ] **Step 6: Прогнать всё** +- [x] **Step 6: Прогнать всё** Run: `bash dsh/tests/run.sh && bash tests/run.sh` → зелёные. -- [ ] **Step 7: Коммит** +- [x] **Step 7: Коммит** ```bash git add dsh/skills/sdd/SKILL.md dsh/tests/sdd-skill.test.mjs dsh/tests/install.test.mjs dsh/README.md @@ -199,39 +199,39 @@ git commit -m "feat(dsh): skill sdd — асинхронный протокол - Consumes: skill `sdd` из Task 2, overlay из Task 1 (в профиле уже включён владельцем). - Produces: транскрипт прогона (диспатчи, статусы, отчёты, ledger) в `design.md`; вывод о режимах 1 и 2. -- [ ] **Step 1: Создать temp-репозиторий и план** +- [x] **Step 1: Создать temp-репозиторий и план** `TRIAL="$(mktemp -d)/trial"`; `git init -b main`, коммит `README.md`; `git checkout -b feat/trial`; файл `docs/superpowers/plans/2026-10-24-trial.md` — один план (require-plan разрешает правки), проект: Python-модуль `src/stats.py`. Три задачи: (1) `mean(values)` — float, `ValueError` на пустом списке, готовый код и тест в плане; (2) `median(values)` — по требованиям для чётной длины среднее двух средних; **в плане намеренно оставлен дефект**: `return xs[len(xs) // 2]`, а тест в плане покрывает только нечётную длину (дефект должен найти ревьюер, не тесты); (3) `describe(values)` — dict `{count, mean, median}` через две функции. Тесты — `python3 -m unittest discover -s tests`. Спека прогона — `SPEC.md` в temp-репо: требования к `mean`/`median`/`describe`, включая «чётная длина — среднее двух средних». -- [ ] **Step 2: Ledger прогона** +- [x] **Step 2: Ledger прогона** `.superpowers/sdd/trial-plan/progress.md`: первая строка — путь плана; таблица сканирования плана (конфликт: план Task 2 противоречит `SPEC.md` — рулинг `R-1`: следовать `SPEC.md`, дефект фиксировать находкой `T2.1` при ревью, а не править план до диспатча); пустой `## Findings`; три строки `Task N: pending`. -- [ ] **Step 3: Диспатч implementer'а Task 1 (режим 1)** +- [x] **Step 3: Диспатч implementer'а Task 1 (режим 1)** `subagent` c `description` «trial task 1», `cwd` = temp-репо, `provider: deepseek-official`, `model: deepseek-flash`; промпт — brief файлом `briefs/task-1.md` (контекст, требования, точный код, тест, коммит), отчёт — `reports/task-1.md` с первой строкой-статусом; требование: `python3 -m unittest discover -s tests` зелёный, коммит. После вызова — закончить ход; продолжить по уведомлению. -- [ ] **Step 4: Собрать отчёт Task 1 и отправить на ревью** +- [x] **Step 4: Собрать отчёт Task 1 и отправить на ревью** Прочитать `reports/task-1.md`, сверить коммит (`git -C "$TRIAL" log --oneline`) и прогон тестов; `list_agents` — сверка состояния; в ledger `Task 1: complete`. Диффпакет — `review/pkg-task-1.txt` (`git diff main...HEAD`); `subagent` с `model: deepseek-v4-pro`: ревью по `SPEC.md` и по качеству, находки `T1.`, вердикт в `reports/review-1.md`. -- [ ] **Step 5: Task 2 — диспатч, ревью, fix-loop** +- [x] **Step 5: Task 2 — диспатч, ревью, fix-loop** Так же диспатчить Task 2 (flash, brief с кодом из плана и дефектом). Ревью (v4-pro) должно вернуть `T2.1` — чётная длина медианы. Fix-раунд 1: `send_message` тому же implementer'у с находкой; по уведомлению — тест на чётную длину, фикс, коммит; ре-ревью скопом — новый `subagent` (v4-pro) с пакетом `review/pkg-fix-1.txt`; в ledger `T2.1: fixed `. Если ревьюер дефект не нашёл — зафиксировать `O-1` (протокол пройден, но приёмка #41 «ревьюер находит дефект» не доказана) и повторить ревью с явным указанием проверять требование «чётная длина». -- [ ] **Step 6: Task 3 и финальное ревью ветки** +- [x] **Step 6: Task 3 и финальное ревью ветки** Task 3 — flash, по плану; ревью — v4-pro; финальное ревью всей ветки — `subagent` v4-pro по полному диффу `main...HEAD`, находки `C-n`/`I-n`/`M-n`; один fix-диспатч при находках, одно скоуп-ревью. -- [ ] **Step 7: Проба режима 2 (глубина 2)** +- [x] **Step 7: Проба режима 2 (глубина 2)** Отдельный temp-репозиторий или новая ветка в том же: `subagent` (flash) с промптом «ты контроллер, задиспатчь implementer'а на задачу X, дождись уведомления, верни отчёт»; контроллер обязан сам вызвать `subagent` и завершиться. Записать: работает / падает `exceeds maxDepth` / уведомление не пришло. При падении — находка `O-n` и вывод, что режим 2 требует поднятой глубины (overlay уже в профиле). -- [ ] **Step 8: Записать результаты** +- [x] **Step 8: Записать результаты** `openspec/changes/dsh-sdd/design.md` — раздел «Результаты пробного прогона 24.10.2026»: таблица приёмки #41 (пункт → чем доказан → статус), реальные диспатчи с моделями, найденный дефект `T2.1` с коммитом фикса, ledger дословно, вывод по режимам, наблюдения `O-n` со статусами. `dsh/PORT.md` §8 п. 4 — закрыт. -- [ ] **Step 9: Проверки, коммит, PR** +- [x] **Step 9: Проверки, коммит, PR** Run: `bash dsh/tests/run.sh && bash tests/run.sh`; `openspec validate dsh-sdd --strict` (если поддерживается). Коммит `docs(dsh): пробный прогон SDD — режимы, дефект и fix-loop (#41)`, push, `tea pulls create --base main --head feat/41-dsh-sdd-spike --title "DSH: SDD — skill, overlay, пробный прогон (#41)"`. Комментарий в #41 — состояние фазы и ссылка на PR. diff --git a/dsh/PORT.md b/dsh/PORT.md index 354fa66..859705a 100644 --- a/dsh/PORT.md +++ b/dsh/PORT.md @@ -1,10 +1,10 @@ # DSH port — design and spike notes -Status: **spike 0.2.0, live guard/require-plan/bootstrap verified; `/role` -command works, the model-invocable `role` skill is added but its live -acceptance is pending**. This directory is the DeepSeek Harness adapter for -`claude-harness`; the Claude Code installation and the repository's source of -truth are unchanged. +Status: **spike 0.2.1, live guard/require-plan/bootstrap verified; `/role` +command works, the model-invocable `role` skill and the asynchronous SDD +protocol are verified live (three-task trial run)**. This directory is the +DeepSeek Harness adapter for `claude-harness`; the Claude Code installation and +the repository's source of truth are unchanged. ## 1. Verdict @@ -60,10 +60,10 @@ file has to be installed as `$DSH_HOME/AGENTS.md` because DSH does not read covers most of `role-hint`. - **`briefing`**: needs an agent preset with read-only tools and a model route; not part of the guard/bootstrap spike. -- **SDD**: the Claude `Task` is synchronous, while DSH `subagent` returns a - child id and reports later. This needs a semantic rewrite (async controller - with `send_message` + `maxDepth: 2`, or a `workflow` script). Do not claim SDD - parity from this spike. +- **SDD parity**: the Claude `Task` is synchronous, while DSH `subagent` returns + a child id and reports later. The `sdd` skill implements the asynchronous + analogue (report files, notification as the wait, `send_message` for fix + rounds, ledger as memory); there is no parity and the skill says so. - **Codegraph by default**: `dsh/mcp.codegraph.yml` is an opt-in overlay; the bundle does not assume the binary is installed. - **`harness-doctor`**: its scripts inspect `~/.claude`; a DSH variant is a @@ -318,10 +318,10 @@ profile was removed; the real `desktop` profile was not touched. 2. Add `harness-check` and optional `role-hint` as non-blocking pre-step injections. 3. Build `briefing` as an agent preset (read-only tools + model route). -4. SDD: change `dsh-sdd` — the `sdd` skill (async controller, reports, review, - fix-loop, ledger) plus the overlay in §5; proven by a three-task trial run in - a temporary repository. See - `openspec/changes/dsh-sdd/design.md`. +4. SDD — **done** (change `dsh-sdd`): the `sdd` skill (async controller, + reports, review, fix-loop, ledger) plus the overlay in §5; proven by a + three-task trial run (`openspec/changes/dsh-sdd/trial/`, results in + `design.md`). 5. Enable CodeGraph MCP by default when the binary is present; update role and project instructions to `mcp__codegraph__codegraph_explore`. 6. Port `harness-doctor` checks to DSH paths. diff --git a/dsh/skills/sdd/SKILL.md b/dsh/skills/sdd/SKILL.md index fc9a99f..433007c 100644 --- a/dsh/skills/sdd/SKILL.md +++ b/dsh/skills/sdd/SKILL.md @@ -53,6 +53,8 @@ overlay ребёнок наследует модель сессии. в контекст. 5. `NEEDS_CONTEXT` — ответить в тот же `send_message`; `BLOCKED` — рулинг контроллера, а не остановка сессии. +6. Замеченное расхождение с брифом или спекой — это `DONE_WITH_CONCERNS`, даже + если задача формально выполнена: ревью его проверит, контроллер — решит. ## Ожидание без блокировки @@ -86,6 +88,10 @@ overlay ребёнок наследует модель сессии. Находка без статуса в отчёте контроллера — ошибка контроллера. +Ожидаемое число тестов в плане — ориентир, а не контракт: fix-раунд добавляет +тесты, поэтому в отчёте фиксируется фактическое число, а расхождение вверх +объясняется, а не считается дефектом. + ## Финальное ревью ветки После всех задач — один ревьюер на весь дифф `main...HEAD`: находки `C-n` @@ -120,7 +126,9 @@ ledger лежит в git-ignored scratch (после `git clean -fdx` восст Маршрут указывается в каждом диспатче: `provider`, `model`, `reasoning_effort`. Параметры действуют только при включённом overlay (`dsh/subagent-models.yml`); без него всё идёт на модели сессии, и отчёт -фиксирует это наблюдением `O-n`. +фиксирует это наблюдением `O-n`. Если `subagent` вернул «child LLM provider +and model must be supplied together», хотя оба параметра переданы, — повтори +вызов один раз: валидация в 0.2.1-rc.3 нестабильна. ## Отличие от Claude-SDD diff --git a/openspec/changes/dsh-sdd/design.md b/openspec/changes/dsh-sdd/design.md index df3b47a..dc428dd 100644 --- a/openspec/changes/dsh-sdd/design.md +++ b/openspec/changes/dsh-sdd/design.md @@ -137,6 +137,73 @@ DoD порта требует, чтобы CC-харнес не менялся. | 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-профиль diff --git a/openspec/changes/dsh-sdd/trial/README.md b/openspec/changes/dsh-sdd/trial/README.md new file mode 100644 index 0000000..b73384c --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/README.md @@ -0,0 +1,45 @@ +# Пробный прогон SDD (24.10.2026) + +Доказательство DSH-семантики из `../design.md`, раздел «Результаты пробного +прогона». Прогон сделан один раз: без DSH runtime его не повторить, поэтому +артефакты лежат здесь. + +## Что прогонялось + +Отдельный git-репозиторий `/tmp/dsh-sdd-trial` (в PR не входит), ветка +`feat/trial`, база `main`. Модуль `src/stats.py` из трёх задач плана +`plan.md` по спеке `SPEC.md`: + +| Задача | Функция | Дефект в плане | +|---|---|---| +| 1 | `mean` | нет | +| 2 | `median` | намеренный: чётная длина считалась как верхний средний элемент (рулинг `R-1`) | +| 3 | `describe` | нет | + +Контроллер — главная сессия (Режим 1). Implementer'ы — `deepseek-flash`, +все ревью — `deepseek-v4-pro`. Overlay профиля включён (model selection + +`maxDepth: 2`). + +## Артефакты + +- `plan.md`, `SPEC.md` — план прогона и его спека; +- `briefs/` — брифы implementer'ов (`task-*.md`) и ревьюеров (`review-*.md`); +- `reports/` — отчёты implementer'ов, ревью задач, fix-раунда, ре-ревью и + финального ревью ветки (первая строка каждого — статус или вердикт); +- `ledger.md` — ledger прогона: `## Plan scan`, `## Tasks`, `## Findings`, + `## Rulings`, `## Mode 2 probe`. + +Результат: `T2.1`/`T2.2` найдены ревьюером Task 2 независимо от concerns +implementer'а, исправлены в `186575c`, ре-ревью чистое; финальное ревью ветки — +APPROVED. Режим 2 (фоновый контроллер) проверен отдельно на +`/tmp/dsh-sdd-mode2`: контроллер вызвал implementer'а, дождался уведомления и +проверил артефакты. + +## Как повторить + +1. Скопировать overlay `dsh/subagent-models.yml` в профиль и перезапустить DSH + (см. `dsh/PORT.md` §5). +2. Скопировать `dsh/skills/sdd` в `~/.dsh/skills` (`bash dsh/install.sh + --skills`). +3. В новой сессии DSH загрузить skill `sdd` и попросить исполнить план: контроллер + создаст ledger, брифы и отчёты в рабочей директории плана. diff --git a/openspec/changes/dsh-sdd/trial/SPEC.md b/openspec/changes/dsh-sdd/trial/SPEC.md new file mode 100644 index 0000000..54f6974 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/SPEC.md @@ -0,0 +1,20 @@ +# Spec: stats + +Module `src/stats.py`. Plain functions over a list of numbers. No third-party +dependencies; Python 3.8+ stdlib only. + +## mean(values) + +Returns the arithmetic mean as `float`. Raises `ValueError("empty")` on an +empty list. + +## median(values) + +Returns the median as `float` and does not mutate `values`. +- Odd length: the middle element. +- Even length: the mean of the two middle elements. +- Empty list: `ValueError("empty")`. + +## describe(values) + +Returns `{"count": int, "mean": float, "median": float}`. diff --git a/openspec/changes/dsh-sdd/trial/briefs/review-1.md b/openspec/changes/dsh-sdd/trial/briefs/review-1.md new file mode 100644 index 0000000..6970fdf --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/briefs/review-1.md @@ -0,0 +1,14 @@ +# Brief: ревью Task 1 + +Репозиторий: `/tmp/dsh-sdd-trial`. +Прочитай `SPEC.md`, Task 1 в `docs/superpowers/plans/2026-10-24-trial.md`, +`reports/task-1.md` и дифф задачи в `review/pkg-task-1.txt`. + +Проверь две вещи независимо: +1. соответствие спеке и плану (сигнатуры, значения, поведение на пустом списке); +2. качество кода и тестов (тесты проверяют поведение, а не реализацию; нет + мёртвого кода и лишних зависимостей). + +Вердикт — в `reports/review-1.md`: строка `VERDICT: APPROVED` или +`VERDICT: CHANGES_REQUESTED`, затем находки `T1.` — маркер, одна строка, +файл:строка и что именно исправить. Нет находок — напиши `Находок нет`. diff --git a/openspec/changes/dsh-sdd/trial/briefs/review-2.md b/openspec/changes/dsh-sdd/trial/briefs/review-2.md new file mode 100644 index 0000000..6a1d2e3 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/briefs/review-2.md @@ -0,0 +1,14 @@ +# Brief: ревью Task 2 + +Репозиторий: `/tmp/dsh-sdd-trial`. +Прочитай `SPEC.md`, Task 2 в `docs/superpowers/plans/2026-10-24-trial.md`, +`reports/task-2.md` и дифф задачи в `review/pkg-task-2.txt`. + +Проверь две вещи независимо: +1. соответствие спеке (не только плану — план мог разойтись со спекой); +2. качество кода и тестов: покрыты ли граничные случаи, которые спека требует. + +Вердикт — в `reports/review-2.md`: `VERDICT: APPROVED` или +`VERDICT: CHANGES_REQUESTED`, затем находки `T2.` — маркер, одна строка, +файл:строка, что исправить и по какому требованию спеки. Нет находок — +`Находок нет`. diff --git a/openspec/changes/dsh-sdd/trial/briefs/review-3.md b/openspec/changes/dsh-sdd/trial/briefs/review-3.md new file mode 100644 index 0000000..1d3a4c3 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/briefs/review-3.md @@ -0,0 +1,11 @@ +# Brief: ревью Task 3 + +Репозиторий: `/tmp/dsh-sdd-trial`. +Прочитай `SPEC.md`, Task 3 в `docs/superpowers/plans/2026-10-24-trial.md`, +`reports/task-3.md` и дифф задачи в `review/pkg-task-3.txt`. + +Проверь соответствие спеке и качество. В том числе: `describe` не дублирует +логику `mean`/`median`, ключи возвращаемого словаря и их типы из спеки. + +Вердикт — в `reports/review-3.md`: `VERDICT: ...`, затем находки `T3.`, +иначе `Находок нет`. diff --git a/openspec/changes/dsh-sdd/trial/briefs/task-1.md b/openspec/changes/dsh-sdd/trial/briefs/task-1.md new file mode 100644 index 0000000..d8b7175 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/briefs/task-1.md @@ -0,0 +1,15 @@ +# Brief: Task 1 — mean + +Репозиторий: `/tmp/dsh-sdd-trial`, ветка `feat/trial`. Работай только в нём. +Прочитай `SPEC.md` и `docs/superpowers/plans/2026-10-24-trial.md`. + +Выполни Task 1 ровно как в плане: создай `src/stats.py` и `tests/test_stats.py` +с кодом из плана (сигнатуры и тела — дословно), прогони +`python3 -m unittest discover -s tests` (ожидается 2 теста OK), закоммить с +сообщением `feat: add mean`. + +Отчёт — файл `reports/task-1.md`. Первая строка отчёта — статус +(`DONE` / `DONE_WITH_CONCERNS` / `NEEDS_CONTEXT` / `BLOCKED`), дальше коммит +(короткий хеш и сообщение), одна строка сводки тестов, concerns, что осталось. + +Не трогай другие задачи, не меняй `SPEC.md` и план, не правь чужие файлы. diff --git a/openspec/changes/dsh-sdd/trial/briefs/task-2.md b/openspec/changes/dsh-sdd/trial/briefs/task-2.md new file mode 100644 index 0000000..1a4e688 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/briefs/task-2.md @@ -0,0 +1,17 @@ +# Brief: Task 2 — median + +Репозиторий: `/tmp/dsh-sdd-trial`, ветка `feat/trial`. Работай только в нём. +Прочитай `SPEC.md` и Task 2 в `docs/superpowers/plans/2026-10-24-trial.md`. + +Выполни Task 2 механически, дословно как в плане: добавь в `src/stats.py` +функцию `median` с телом из плана, добавь в `tests/test_stats.py` класс +`MedianTest` и импорт `median`, прогони +`python3 -m unittest discover -s tests` (ожидается 4 теста OK), закоммить с +сообщением `feat: add median`. + +Контракт реализации — план, а не собственная интерпретация: если код из плана +кажется неверным, всё равно внеси его дословно, а сомнение напиши в concerns +отчёта. Спор с планом решает контроллер, не ты. + +Отчёт — `reports/task-2.md`: первая строка — статус, дальше коммит, одна строка +сводки тестов, concerns. diff --git a/openspec/changes/dsh-sdd/trial/briefs/task-3.md b/openspec/changes/dsh-sdd/trial/briefs/task-3.md new file mode 100644 index 0000000..972c2d6 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/briefs/task-3.md @@ -0,0 +1,12 @@ +# Brief: Task 3 — describe + +Репозиторий: `/tmp/dsh-sdd-trial`, ветка `feat/trial`. Работай только в нём. +Прочитай `SPEC.md` и Task 3 в `docs/superpowers/plans/2026-10-24-trial.md`. + +Выполни Task 3 дословно как в плане: добавь `describe` в `src/stats.py`, +добавь `DescribeTest` и импорт `describe` в `tests/test_stats.py`, прогони +`python3 -m unittest discover -s tests` (ожидается 5 тестов OK), закоммить с +сообщением `feat: add describe`. + +Отчёт — `reports/task-3.md`: первая строка — статус, дальше коммит, одна строка +сводки тестов, concerns. diff --git a/openspec/changes/dsh-sdd/trial/ledger.md b/openspec/changes/dsh-sdd/trial/ledger.md new file mode 100644 index 0000000..5dcf4e2 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/ledger.md @@ -0,0 +1,77 @@ +docs/superpowers/plans/2026-10-24-trial.md + +# Ledger: trial (пробный прогон SDD, issue #41) + +## Plan scan + +| # | Конфликт | Решение | Рулинг | +|---|---|---|---| +| R-1 | План Task 2 противоречит `SPEC.md`: для чётной длины возвращает верхний средний элемент, а спека требует среднее двух средних | implementer Task 2 выполняет план буквально — прогон проверяет, что ревью ловит расхождение плана со спекой; исправление по спеке в fix-loop | R-1 (design #41) | + +## Tasks + +- Task 1: pending +- Task 2: pending +- Task 3: pending + +## Findings + +(пусто) + +## Rulings + +- **R-1** (design #41): план Task 2 расходится со спекой для чётной длины; + план исполняется буквально, находка `T2.1` ожидается на ревью Task 2. + Цена ошибки: если ревьюер не заметит — приёмка «ревьюер находит дефект» не + доказана, прогон фиксирует это наблюдением и повторяет ревью. + +## Dispatches + +- Task 1: implementer `1c906f6f-c542-452d-9695-a5c2e31d8869`, deepseek-flash, dispatched 17:4x +- Task 1: complete dc1670f (2 tests OK), ревью отправлено +- Task 1: review APPROVED (68a1aeb4), находок нет +- Task 2: implementer `1ec8f7fc-f03e-4a90-ad78-32d5f410eaca`, deepseek-flash, dispatched + +## Findings + +(пусто; ниже — наблюдения, не находки ревью) + +- **O-1**: ревьюер Task 1 заметил, что «Interfaces» плана обещает аннотации + `list[float] -> float`, а «Exact code» их не содержит; SPEC аннотаций не + требует. Дефект плана, не кода — статус `rejected` (правка плана в прогоне + не имеет смысла). +- **O-2**: первый вызов `subagent` с `provider: deepseek-official` и + `model: deepseek-flash` вернул ошибку «child LLM provider and model must be + supplied together», хотя оба параметра переданы; повторный вызов с теми же + значениями прошёл. Наблюдение для порта: ошибка валидации нестабильна. +- Task 2: complete 836a730 (4 tests OK; implementer сам отметил расхождение со + спекой в concerns), ревью отправлено (e50c74d2, deepseek-v4-pro) +- **O-3**: ошибка «child LLM provider and model must be supplied together» + повторяется на первом вызове `subagent` в ходе (дважды за прогон), хотя + provider/model переданы; немедленный повтор с теми же аргументами проходит. + Статус: наблюдение, влияет на надёжность диспатча в режиме 2. +- Task 2: review CHANGES_REQUESTED (e50c74d2) — T2.1 (медиана чётной длины), + T2.2 (нет теста на чётную длину); fix-раунд 1 → тот же implementer 1ec8f7fc + +## Findings + +- **T2.1** — `median` при чётной длине возвращает верхний средний элемент + (`float(xs[n // 2])`); SPEC требует среднее двух средних. open +- **T2.2** — нет теста на чётную длину, нарушение T2.1 не ловится. open +- Task 2: fix-раунд 1 → 186575c (6 tests OK), T2.1/T2.2 awaiting re-review +- Task 2: re-review APPROVED (7912c16b), T2.1/T2.2 fixed 186575c; Task 2: complete 186575c +- Task 3: complete 7cb0383 (7 tests OK), review APPROVED (f10e3165), находок нет +- **O-4**: ожидаемое число тестов в плане (5) устарело после fix-раунда Task 2 + (фактически 7); implementer и ревьюер зафиксировали расхождение, ложным + дефектом не считали. Урок для skill: счётчик тестов в плане — ориентир. +- **O-5**: тест describe([1,2,3]) на симметричном входе (mean == median) не + отличил бы перестановку mean/median; реализация верна, слабое место плана. + Статус: наблюдение, в прогоне не правится. + +## Mode 2 probe (отдельная площадка /tmp/dsh-sdd-mode2) + +- фоновый контроллер `afe886e5` (deepseek-flash) → implementer `c79a625f` + (deepseek-flash): вызов `subagent` из контроллера прошёл, depth 2 работает; +- контроллер закончил ход и продолжил по уведомлению; +- implementer: DONE, коммит 0601d6e, 1 тест OK; контроллер проверил отчёт, + коммит и тесты независимо. diff --git a/openspec/changes/dsh-sdd/trial/plan.md b/openspec/changes/dsh-sdd/trial/plan.md new file mode 100644 index 0000000..49eca8b --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/plan.md @@ -0,0 +1,125 @@ +# Trial: stats — Implementation Plan + +**Goal:** three-function statistics module used as the SDD trial run (issue #41). + +**Spec:** `SPEC.md`. + +**Ветка:** `feat/trial`. + +## Global Constraints + +- Python 3.8+ stdlib only; no dependencies. +- Tests: `python3 -m unittest discover -s tests` from the repository root. +- The implementer of a task makes the commit for that task. + +## Task 1: mean + +**Files:** +- Create: `src/stats.py` +- Create: `tests/test_stats.py` + +**Interfaces:** +- Produces: `mean(values: list[float]) -> float`; `ValueError("empty")` on `[]`. + +`src/stats.py`: + +```python +"""Small statistics helpers (trial module).""" + + +def mean(values): + if not values: + raise ValueError("empty") + return float(sum(values)) / len(values) +``` + +`tests/test_stats.py`: + +```python +import unittest + +from src.stats import mean + + +class MeanTest(unittest.TestCase): + def test_mean(self): + self.assertEqual(mean([1, 2, 3]), 2.0) + + def test_mean_empty(self): + with self.assertRaises(ValueError): + mean([]) + + +if __name__ == "__main__": + unittest.main() +``` + +Run: `python3 -m unittest discover -s tests` → 2 tests OK. +Commit: `feat: add mean`. + +## Task 2: median + +**Files:** +- Modify: `src/stats.py` (append `median`) +- Modify: `tests/test_stats.py` (append `MedianTest`, import `median`) + +**Interfaces:** +- Consumes: nothing from Task 1. +- Produces: `median(values: list[float]) -> float`; `ValueError("empty")` on `[]`. + +Exact code to append to `src/stats.py`: + +```python +def median(values): + xs = sorted(values) + if not xs: + raise ValueError("empty") + n = len(xs) + return float(xs[n // 2]) +``` + +Test to append to `tests/test_stats.py` (class `MedianTest`): + +```python +class MedianTest(unittest.TestCase): + def test_median_odd(self): + self.assertEqual(median([3, 1, 2]), 2.0) + + def test_median_empty(self): + with self.assertRaises(ValueError): + median([]) +``` + +Run: `python3 -m unittest discover -s tests` → 4 tests OK. +Commit: `feat: add median`. + +## Task 3: describe + +**Files:** +- Modify: `src/stats.py` (append `describe`) +- Modify: `tests/test_stats.py` (append `DescribeTest`, import `describe`) + +**Interfaces:** +- Consumes: `mean` (Task 1), `median` (Task 2). +- Produces: `describe(values: list[float]) -> dict` with keys `count`, `mean`, `median`. + +Exact code to append to `src/stats.py`: + +```python +def describe(values): + return {"count": len(values), "mean": mean(values), "median": median(values)} +``` + +Test to append (class `DescribeTest`): + +```python +class DescribeTest(unittest.TestCase): + def test_describe(self): + self.assertEqual( + describe([1, 2, 3]), + {"count": 3, "mean": 2.0, "median": 2.0}, + ) +``` + +Run: `python3 -m unittest discover -s tests` → 5 tests OK. +Commit: `feat: add describe`. diff --git a/openspec/changes/dsh-sdd/trial/reports/fix-2-round-1.md b/openspec/changes/dsh-sdd/trial/reports/fix-2-round-1.md new file mode 100644 index 0000000..9b80a08 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/reports/fix-2-round-1.md @@ -0,0 +1,31 @@ +DONE + +## Коммит + +- `186575c` — `fix: median even-length per spec` +- Ветка: `feat/trial`, родитель: `836a730` (`feat: add median`) +- Файлы: `src/stats.py` (+2 строки), `tests/test_stats.py` (+6 строк); `git status` чистый + +## Что исправлено + +- **T2.1** — `src/stats.py`: при чётном `n` возвращается + `float(xs[n // 2 - 1] + xs[n // 2]) / 2`; нечётная ветка, проверка пустого + списка и отсутствие мутации не тронуты. +- **T2.2** — `tests/test_stats.py`: добавлены `MedianTest.test_median_even_two` + (`median([1, 2]) == 1.5`) и `MedianTest.test_median_even_four` + (`median([1, 2, 3, 4]) == 2.5`). + +## Тесты + +`python3 -m unittest discover -s tests` → `Ran 6 tests ... OK` (exit 0): +`MeanTest.test_mean`, `MeanTest.test_mean_empty`, `MedianTest.test_median_odd`, +`MedianTest.test_median_empty`, `MedianTest.test_median_even_two`, +`MedianTest.test_median_even_four`. + +## Что осталось + +- Находок T2.1/T2.2 больше нет; доп. проверка: `median([4, 1, 3, 2]) == 2.5` + (несортированный вход), исходный список не мутирует. +- Task 3 (`describe`) — по плану, не моя задача. +- Отчёт лежит в `reports/fix-2-round-1.md`; `reports/` в `.gitignore`, в коммит + не входит. diff --git a/openspec/changes/dsh-sdd/trial/reports/re-review-2.md b/openspec/changes/dsh-sdd/trial/reports/re-review-2.md new file mode 100644 index 0000000..a38fc87 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/reports/re-review-2.md @@ -0,0 +1,4 @@ +VERDICT: APPROVED + +T2.1: fixed 186575c +T2.2: fixed 186575c diff --git a/openspec/changes/dsh-sdd/trial/reports/review-1.md b/openspec/changes/dsh-sdd/trial/reports/review-1.md new file mode 100644 index 0000000..0d7f64b --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/reports/review-1.md @@ -0,0 +1,31 @@ +# Review: Task 1 (mean) + +## Проверено + +- Коммит `dc1670f` (`feat: add mean`) на ветке `feat/trial`, родитель `0870c6d`; + `git status` чистый, в коммите только `src/stats.py` и `tests/test_stats.py`. +- Содержимое обоих файлов дословно совпадает с «Exact code» из Task 1 плана + `docs/superpowers/plans/2026-10-24-trial.md`. +- `python3 -m unittest discover -s tests` → `Ran 2 tests ... OK` (exit 0). + +## Соответствие спеке и плану + +- Сигнатура `mean(values)`, возврат `float`: `mean([1, 2, 3]) == 2.0` — верно. +- Пустой список: `ValueError("empty")` — верно (сообщение в реализации + совпадает со спекой; тест проверяет только тип исключения, что соответствует + плану). +- Аннотации `list[float] -> float` из строки «Interfaces» в самом плане + отсутствуют и в «Exact code»; SPEC аннотаций не требует. Несоответствие — в + плане, не в реализации; действию не подлежит. + +## Качество кода и тестов + +- Тесты проверяют поведение (значение mean и ValueError на пустом списке), а + не реализацию. +- Мёртвого кода и сторонних зависимостей нет. + +## Вердикт + +VERDICT: APPROVED + +Находок нет diff --git a/openspec/changes/dsh-sdd/trial/reports/review-2.md b/openspec/changes/dsh-sdd/trial/reports/review-2.md new file mode 100644 index 0000000..b16e881 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/reports/review-2.md @@ -0,0 +1,35 @@ +# Review: Task 2 (median) + +Ревьюер проверял поведение по `SPEC.md` независимо, не полагаясь на concerns +implementer'а. + +## Проверенные требования SPEC.md + +| Требование | Результат | +|---|---| +| Нечётная длина — средний элемент | OK: `median([3,1,2]) == 2.0` | +| Чётная длина — среднее двух средних | **НАРУШЕНО** | +| Пустой список — `ValueError("empty")` | OK: тип и сообщение совпадают | +| Не мутирует `values` | OK: `sorted()` копирует, вход не меняется | +| Возвращает `float` | OK | + +## Тесты + +`python3 -m unittest discover -s tests` → `Ran 4 tests ... OK` (exit 0). +4 теста проходят, но чётная длина не покрыта, поэтому нарушение спеки не +ловится. + +## Findings + +- `T2.1` — `src/stats.py:15` — при чётной длине `median` возвращает верхний + средний элемент `float(xs[n // 2])`, а `SPEC.md:15` требует среднее двух + средних: `median([1,2])` → `2.0` вместо `1.5`, `median([1,2,3,4])` → `3.0` + вместо `2.5`. Исправить: для чётного `n` вернуть + `float(xs[n//2 - 1] + xs[n//2]) / 2`. +- `T2.2` — `tests/test_stats.py` — нет теста на чётную длину, поэтому + нарушение `T2.1` не ловится. Добавить тест `median([1,2]) == 1.5` (и + `median([1,2,3,4]) == 2.5`) по требованию `SPEC.md:15`. + +## VERDICT: CHANGES_REQUESTED + +Находки: `T2.1`, `T2.2`. diff --git a/openspec/changes/dsh-sdd/trial/reports/review-3.md b/openspec/changes/dsh-sdd/trial/reports/review-3.md new file mode 100644 index 0000000..2436b5e --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/reports/review-3.md @@ -0,0 +1,54 @@ +# Review Task 3: describe + +**Вердикт:** `VERDICT: APPROVED` + +## Что проверено + +- **Спека (`SPEC.md`):** `describe(values)` → `{"count": int, "mean": float, "median": float}`. +- **План Task 3:** дописать `describe` и `DescribeTest`, код «дословно из плана». +- **Дифф `review/pkg-task-3.txt`** и фактические файлы на `feat/trial` (коммит `7cb0383`). + +## Соответствие спеке + +- `describe` **не дублирует** логику `mean`/`median` — вызывает их напрямую: + `{"count": len(values), "mean": mean(values), "median": median(values)}`. +- Ключи словаря: `count`, `mean`, `median` — ровно по спеке, лишних/недостающих нет. +- Типы: `count` — `int` (результат `len`); `mean` — `float` (возврат `mean`); `median` — + `float` (возврат `median`). Сходится с `{"count": int, "mean": float, "median": float}`. +- Пустой список: `describe([])` бросает `ValueError("empty")` через делегирование в + `mean`/`median` — поведение согласовано с модулем, спека отдельного контракта для + `describe` на `[]` не задаёт. + +## Соответствие плану + +- Код `src/stats.py` и `tests/test_stats.py` совпадает с «Exact code» Task 3 дословно. +- Импорт `describe` добавлен, класс `DescribeTest.test_describe` присутствует. + +## Проверка теста Task 3 (ловит поведение, а не повторяет реализацию) + +Тест сравнивает `describe([1, 2, 3])` с **независимыми константами** +`{"count": 3, "mean": 2.0, "median": 2.0}`, вычисленными вручную из спеки, а не +через вызовы `len`/`mean`/`median` внутри теста. То есть это оракул, а не тавтология: +он ловит неверные ключи (переименование/лишние/недостающие), неверный `count` и +неверные значения `mean`/`median`. + +**Наблюдение (не блокирует):** вход `[1, 2, 3]` симметричен — `mean == median == 2.0`, +поэтому тест не отличил бы перестановку `mean`/`median` местами. Это слабость +теста, но она зафиксирована самим планом («Exact code»), реализация при этом +корректна и читаема (`mean` для `mean`, `median` для `median`). Как дефект не считаю. + +## Прогон тестов + +``` +Ran 7 tests in 0.000s ... OK +``` + +`git status` чистый; лишних файлов в коммите нет. + +## Находки + +Находок нет. + +--- + +VERDICT: APPROVED — Находок нет diff --git a/openspec/changes/dsh-sdd/trial/reports/review-final.md b/openspec/changes/dsh-sdd/trial/reports/review-final.md new file mode 100644 index 0000000..55fe46b --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/reports/review-final.md @@ -0,0 +1,46 @@ +# Финальное ревью ветки feat/trial + +Ревьюер проверил ветку целиком: `main` (0870c6d) ... `HEAD` (7cb0383), 4 коммита. + +## Проверено + +- **Дифф ветки** (`git diff main...HEAD`): только `src/stats.py` (21 строка) и + `tests/test_stats.py` (39 строк), оба добавлены. Посторонних изменений нет: + `reports/`, `briefs/`, `review/`, `.superpowers/`, `__pycache__/` в `.gitignore` + и в коммиты не попали. `git status` чистый. +- **Коммиты**: `dc1670f` (mean) → `836a730` (median) → `186575c` (fix) → `7cb0383` + (describe), по одному на задачу + fix-раунд; реализатор каждой задачи коммитил + сам (по плану). +- **Прогон**: `python3 -m unittest discover -s tests` → `Ran 7 tests ... OK` + (exit 0), как ожидалось. + +## Соответствие SPEC.md + +| Требование | Результат | +|---|---| +| `mean` — среднее как `float`; `ValueError("empty")` на `[]` | OK (`src/stats.py:4-7`) | +| `median` нечётная длина — средний элемент | OK (`src/stats.py:17`) | +| `median` чётная длина — среднее двух средних | OK (`src/stats.py:15-16`) | +| `median` не мутирует `values` | OK: `sorted()` копирует (`src/stats.py:11`) | +| `median` пустой список — `ValueError("empty")` | OK (`src/stats.py:12-13`) | +| `median` возвращает `float` | OK | +| `describe` — `{"count": int, "mean": float, "median": float}` | OK (`src/stats.py:20-21`) | + +## Качество и покрытие тестами + +- Тесты проверяют поведение по независимым константам-оракулам, а не повторяют + реализацию. Покрыты: mean (значение + пустой), median (нечётная, пустой, чётная + 2 элемента, чётная 4 элемента), describe (ключи + типы + значения). +- Мёртвого кода и сторонних зависимостей нет; stdlib только. +- T2.1 (чётная длина) и T2.2 (нет теста на чётную длину) исправлены в `186575c` + и подтверждены ре-ревью (`reports/re-review-2.md`: APPROVED, fixed 186575c) — + переоткрывать без причины не стал; фактическая реализация и тесты соответствуют + спеке. + +## Находки + +Находок нет. + +--- + +VERDICT: APPROVED — Находок нет diff --git a/openspec/changes/dsh-sdd/trial/reports/task-1.md b/openspec/changes/dsh-sdd/trial/reports/task-1.md new file mode 100644 index 0000000..a20dae1 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/reports/task-1.md @@ -0,0 +1,20 @@ +DONE + +## Коммит + +- `dc1670f` — `feat: add mean` +- Ветка: `feat/trial`, родитель: `0870c6d` (`chore: trial plan, spec and skeleton`) +- Файлы: `src/stats.py`, `tests/test_stats.py` (созданы, код дословно из плана Task 1) + +## Тесты + +`python3 -m unittest discover -s tests` → `Ran 2 tests ... OK` (exit 0): `MeanTest.test_mean`, `MeanTest.test_mean_empty`. + +## Concerns + +Нет. Замечания (не требуют действий): в `src/` нет `__init__.py` — импорт `from src.stats import mean` работает как namespace-пакет при запуске из корня репозитория, т.е. ровно предписанной планом командой; вне корня (или при другой top-level dir) импорт сломается. `sorted()`/`median`/`describe` из SPEC намеренно отсутствуют — это Task 2 и Task 3. + +## Что осталось + +- Task 2 (`median`) и Task 3 (`describe`) — по плану, не мои задачи; `src/stats.py` и `tests/test_stats.py` готовы к дополнению. +- Отчёт лежит в `reports/task-1.md`; каталог `reports/` в `.gitignore`, поэтому в коммит не входит. diff --git a/openspec/changes/dsh-sdd/trial/reports/task-2.md b/openspec/changes/dsh-sdd/trial/reports/task-2.md new file mode 100644 index 0000000..b902b39 --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/reports/task-2.md @@ -0,0 +1,29 @@ +DONE + +## Коммит + +- `836a730` — `feat: add median` +- Ветка: `feat/trial`, родитель: `dc1670f` (`feat: add mean`) +- Файлы: `src/stats.py` (дописан `median`), `tests/test_stats.py` (импорт `median`, класс `MedianTest`) — код дословно из плана Task 2 +- `git status` чистый; в коммит не попали `reports/` (в `.gitignore`) и `__pycache__` + +## Тесты + +`python3 -m unittest discover -s tests` → `Ran 4 tests ... OK` (exit 0): `MeanTest.test_mean`, `MeanTest.test_mean_empty`, `MedianTest.test_median_odd`, `MedianTest.test_median_empty`. + +## Concerns + +1. **Расхождение плана со SPEC (важное, не исправлял — по брифу план дословен).** + Реализация из плана возвращает `float(xs[n // 2])` и для чётной длины берёт + верхний средний элемент, а SPEC требует среднее двух средних: + - `median([1, 2])` → `2.0`, по SPEC `1.5`; + - `median([1, 2, 3, 4])` → `3.0`, по SPEC `2.5`. + Тесты плана чётную длину не проверяют, поэтому 4 теста проходят и нарушение + спеки не ловится. Решение — за контроллером: чинить план/код и добавлять + тест на чётную длину отдельной задачей. +2. `median` не мутирует вход: `sorted()` возвращает копию — требование SPEC + «does not mutate values» выполнено. +3. В разделе «Interfaces» плана заявлены аннотации `list[float] -> float`, в + «Exact code» их нет (как и в Task 1); внёс дословно, без аннотаций. +4. `ValueError("empty")` на пустом списке — есть, сообщение совпадает со SPEC; + тест проверяет только тип исключения (по плану). diff --git a/openspec/changes/dsh-sdd/trial/reports/task-3.md b/openspec/changes/dsh-sdd/trial/reports/task-3.md new file mode 100644 index 0000000..3f2213d --- /dev/null +++ b/openspec/changes/dsh-sdd/trial/reports/task-3.md @@ -0,0 +1,33 @@ +DONE + +## Коммит + +- `7cb0383` — `feat: add describe` +- Ветка: `feat/trial`, родитель: `186575c` (`fix: median even-length per spec`) +- Файлы: `src/stats.py` (дописан `describe`), `tests/test_stats.py` (импорт `describe`, класс `DescribeTest`) — код дословно из плана Task 3 +- `git status` чистый после коммита; `reports/`, `briefs/`, `review/` и `__pycache__` в `.gitignore`, в коммит не попали + +## Тесты + +`python3 -m unittest discover -s tests` → `Ran 7 tests in 0.000s ... OK` (exit 0), в т.ч. `DescribeTest.test_describe`, `MedianTest.test_median_even_two`, `MedianTest.test_median_even_four`. + +## Concerns + +1. **Счёт тестов в брифе/плане устарел (наблюдение, не дефект кода).** + Бриф и план Task 3 ожидают «5 tests OK». Фактически на входе было 6 тестов: + после Task 2 коммит `186575c` (`fix: median even-length per spec`) добавил + `test_median_even_two` и `test_median_even_four`, которых в плане Task 2 не + было. Task 3 добавляет один тест → **7**, все проходят. Реализацию это не + меняет; цифру «5» стоит поправить в плане/брифе, чтобы следующий прогон не + принял 7 за расхождение. +2. `describe` дословно из плана не проверяет пустой список сам — но + `mean([])`/`median([])` бросают `ValueError("empty")`, так что поведение на + `[]` согласовано с SPEC по факту (и покрыто тестами `mean`/`median`). + Отдельного теста на `describe([])` план не требует, не добавлял. +3. `describe` возвращает `count` как `int` (`len`), `mean`/`median` — как + `float`: ключи и типы совпадают с SPEC `{"count": int, "mean": float, "median": float}`. +4. Аннотаций типов в коде нет — как и в Task 1/2, план в «Exact code» их не даёт, + хотя «Interfaces» их упоминает. Внёс дословно. +5. `src/` без `__init__.py` — импорт `from src.stats import ...` работает только + при запуске из корня репозитория namespace-пакетом, т.е. ровно предписанной + командой. Не мой скоуп, менять не стал. -- 2.47.2