diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 08075e6..805b3b3 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -8,12 +8,12 @@ { "name": "av-dev-pm", "source": "./av-dev-pm", - "description": "Управление продуктом: канон документов проекта, задачи и цели вместо приоритетов, спринт под одну цель с заморозкой набора, старт проекта интервью по брифу и приведение существующего к канону." + "description": "Управление продуктом: канон документов проекта, задачи и цели вместо приоритетов, спринт под одну цель с заморозкой набора, старт проекта интервью по брифу и приведение существующего к канону. Ничего не выполняет сам и никакого пайплайна не требует: задача выполняется чем угодно, а канон описывает документы, из которых конвейер ревью берёт проектную конкретику." }, { "name": "av-dev-pipeline", "source": "./av-dev-pipeline", - "description": "Проведение задачи через цикл SDD и конвейер ревью с обязательным триажем, плюс прогон нескольких задач разом. Требует OpenSpec; проектная специфика — из документов канона." + "description": "Проведение задачи через цикл SDD и конвейер ревью с обязательным триажем, плюс прогон нескольких задач разом. Требует OpenSpec. Задача принимается и обычным текстом; плагин av-dev-pm опционален — он даёт документы канона для проходов ревью и учёт задач, без него прогон деградирует поразрядно и говорит об этом." }, { "name": "av-dev-git", diff --git a/DECISIONS.md b/DECISIONS.md index 40039f8..a1fa6ca 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -809,3 +809,54 @@ pyrefly: в окружении нет ничего, кроме линтеров, 46. **Два прохода по разным предметам дороже одного, но не вдвое.** Перекрытие оказалось ровно в одной находке из двадцати — той самой, что подтвердилась дважды. Практика остаётся: ревью на плагин, а не одно на репозиторий. + +## 11. Зависимости между плагинами (2026-08-03) + +### Целевая картина, которую проверяли + +`av-dev-git` ни от чего не зависит. `av-dev-pipeline` сам по себе: задача +приходит **и обычным текстом**, и из `tasks`. `av-dev-pm` оперирует абстрактным +«сделать задачу» и не знает, чем она выполняется. + +### Что показала проверка + +**LL. Первые две цели выполняются, третья в исходной формулировке недостижима — +и формулировку надо поправить, а не картину.** `av-dev-pm` **владеет +конфигурационным файлом конвейера**: `docs/review.md` держит «Вопросы к +проходам» и «Триггеры профиля», то есть перечисляет проходы поимённо, а скелет +`review.md` несёт форму журнала дефектов. Кто-то этим словарём владеть обязан — +канон и есть схема данных, которую конвейер читает. Честная формулировка цели: +**`av-dev-pm` не зовёт пайплайн и не требует его наличия**. Она выполняется. + +**MM. Настоящая протечка была одна — необъявленная деградация опор приёмки.** +«Стимулы» в `session` и приёмка в `sprint.md` держались на «сохранённом отчёте +триажа» по конкретному OpenSpec-пути. В проекте без конвейера ревью защита от +занижения урожая исчезала **молча**: сверять не с чем, а текст об этом не +говорил. Теперь опора названа абстрактно («независимый отчёт ревью»), путь +`av-dev-pipeline` дан как частный случай, а отсутствие конвейера обязано +попадать строкой в доклад спринта. + +**NN. Ветка деградации шага 9 была неисполнима — ровно в том случае, ради +которого написана.** «Плагина нет — открой +`av-dev-pm/skills/canon/references/canon.md`»: путь в дерево маркетплейса, из +проекта без установленного плагина не разрешается ниоткуда. Кросс-плагинные +пути в дерево маркетплейса теперь не используются вообще: пайплайн ходит в +**свой** `references/project-facts.md`, а ссылки в чужой плагин даются через +`Skill <плагин>:<скилл>`. + +### Что из этого следует + +47. **Знаниевый цикл есть и он законен, но каждый его контракт обязан иметь + единственный дом.** Пайплайн описывает раскладку `pm`, `pm` описывает + артефакты пайплайна — пять симметричных контрактов, из них два уже + разошлись: форма журнала дефектов (шесть полей против пяти, «Причина» + потеряна) и список читателей `docs/research/` (`specs` выпал). Дома + назначены: форма журнала — у конвейера, список читателей — у канона; в обеих + копиях стоит явное указание на дом. +48. **Пайплайн больше не называет внутренние имена файлов `pm`.** `items/.md` + и `SPRINT.md` в его тексте были вторым домом для раскладки, которую проект + вправе переименовать через `docs/.pm.json`. +49. **Описания плагинов в манифестах врали умолчанием.** Ни `marketplace.json`, + ни `plugin.json` не говорили, что `av-dev-pm` для конвейера **опционален**, а + задача принимается текстом. Теперь говорят — это первое, что читает человек, + выбирая, что подключать. diff --git a/TODO.md b/TODO.md index c415c35..3b09785 100644 --- a/TODO.md +++ b/TODO.md @@ -94,6 +94,16 @@ - [x] остальные 12 находок: `sprint.md`, «9а», перечень проектных копий, параллельность в батче, триаж в финальной сверке, триггеры профиля, 8–12 +### 1.9 Ревью зависимостей между плагинами (тема 11) + +- [x] опоры приёмки названы абстрактно, деградация без конвейера объявлена (MM) +- [x] ветка деградации шага 9 ходит в свой `project-facts.md` (NN) +- [x] `docs` даёт ветку «конвейера нет» для журнала и промоута +- [x] форма журнала дефектов сведена к дому, копия помечена в `changelog.md` +- [x] `specs` вернулся в читатели `docs/research/`; дом списка назначен +- [x] пайплайн не называет `items/` и `SPRINT.md` — их знает `av-dev-pm` +- [x] манифесты объявили `av-dev-pm` опциональным для конвейера (49) + ## 2. healthlog — первая боевая проверка - [ ] `canon adopt`; `docs/backlog/` → `docs/tasks/` diff --git a/av-dev-pipeline/.claude-plugin/plugin.json b/av-dev-pipeline/.claude-plugin/plugin.json index 7abb1d8..da2efcb 100644 --- a/av-dev-pipeline/.claude-plugin/plugin.json +++ b/av-dev-pipeline/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "av-dev-pipeline", - "description": "Проведение задачи через полный цикл Spec Driven Development и конвейер ревью с детерминированным гейтом, сверкой со спеками, враждебными постановками, эксплуатационным постмортемом, независимой реализацией и обязательным триажем; плюс прогон нескольких задач разом по одной в изолированном worktree. Требует OpenSpec. Проектную конкретику проходы читают в документах канона плагина av-dev-pm напрямую.", + "description": "Проведение задачи через полный цикл Spec Driven Development и конвейер ревью с детерминированным гейтом, сверкой со спеками, враждебными постановками, эксплуатационным постмортемом, независимой реализацией и обязательным триажем; плюс прогон нескольких задач разом по одной в изолированном worktree. Требует OpenSpec. Задача принимается и обычным текстом. Плагин av-dev-pm опционален: он даёт документы канона, из которых проходы читают проектную конкретику, и учёт задач; без него прогон деградирует поразрядно и называет это строкой.", "author": { "name": "Anton Vakhrushev", "email": "anwinged@gmail.com" diff --git a/av-dev-pipeline/skills/review-pipeline/references/project-facts.md b/av-dev-pipeline/skills/review-pipeline/references/project-facts.md index b27ac44..4b5b25e 100644 --- a/av-dev-pipeline/skills/review-pipeline/references/project-facts.md +++ b/av-dev-pipeline/skills/review-pipeline/references/project-facts.md @@ -54,11 +54,16 @@ список и **не сливает в одну строку**: разные пробелы чинятся разным — периметр пишется руками за десять минут, а числа требуют замера. +**Кто какой документ читает — не здесь.** Полный список читателей ведёт канон +(`Skill av-dev-pm:canon`, его `references/canon.md`, таблица «Кто читает»); ниже — +только **последствие** отсутствия, и оно называет самое дорогое, а не всех +пострадавших. Два списка читателей уже однажды разошлись; второго раза не надо. + | Нет документа | Что деградирует | | --- | --- | | `CLAUDE.md` без инвариантов | `critical` по основанию «нарушен инвариант проекта» не присваивается никем | | `docs/security.md` | `adversary` не знает периметра — формулирует условиями, `critical` не ставит | -| `docs/research/` | числа неизвестны `ops`, `adversary`, `reimpl` — все трое формулируют условиями | +| `docs/research/` | числа неизвестны `specs`, `ops`, `adversary`, `reimpl` — формулируют условиями, а `specs` теряет проверку «требование против наблюдения» | | `docs/database.md` | замер не с чем сравнить: находка не поднимается выше гипотезы | | `docs/passport.md` | `architecture` теряет границу домена и вырождается в общее мнение | | `docs/review.md` | `triage` отсеивает вслепую: типовых ложноположительных нет | diff --git a/av-dev-pipeline/skills/review-pipeline/references/review-journal.md b/av-dev-pipeline/skills/review-pipeline/references/review-journal.md index 3c47fdc..60b7a52 100644 --- a/av-dev-pipeline/skills/review-pipeline/references/review-journal.md +++ b/av-dev-pipeline/skills/review-pipeline/references/review-journal.md @@ -40,6 +40,12 @@ ## Форма записи +**Это дом формы, и у него есть копия.** Скелет `docs/review.md`, который кладёт +в проект `av-dev-pm` (`skills/canon/references/skeletons.md`), повторяет её +дословно — он уезжает в репозиторий и обязан там что-то говорить. Правка формы +здесь **обязана** тянуть правку скелета и запись в журнал версий канона; иначе +проекты продолжат писать по старой форме, а конвейер — ждать поля, которого нет. + ``` ## ГГГГ-ММ-ДД — <краткое последствие> [проскочил|пойман] diff --git a/av-dev-pipeline/skills/task-pipeline/SKILL.md b/av-dev-pipeline/skills/task-pipeline/SKILL.md index b3b2713..66a2c45 100644 --- a/av-dev-pipeline/skills/task-pipeline/SKILL.md +++ b/av-dev-pipeline/skills/task-pipeline/SKILL.md @@ -276,8 +276,8 @@ description: Автономно проводит одну задачу чере ### 9. Синк документации Ревью выполненного — до этого шага. Затем **вызови Skill `av-dev-pm:docs`**: он -владеет содержимым документов канона и ведёт чек-лист синка. Плагина нет — -пройди чек-лист сам по списку ниже. +владеет содержимым документов канона и ведёт чек-лист синка. Плагина нет — шаг +всё равно делается, см. ниже. **Правило одно и оно жёсткое: принуждённое отрицание.** Доклад обязан назвать **каждый** документ канона — либо чем он обновлён, либо «не требуется, потому @@ -287,8 +287,15 @@ description: Автономно проводит одну задачу чере **Список документов и их триггеров здесь не дублируется** — он в чек-листе скилла `av-dev-pm:docs`, и копия уже однажды разошлась с оригиналом, потеряв два -триггера. Плагина нет — открой канон (`av-dev-pm/skills/canon/references/canon.md`) -и пройди по разделам его раскладки: каждый документ канона получает строку. +триггера. + +**Плагина `av-dev-pm` в проекте нет** — путь в его дерево не разрешится ниоткуда, +поэтому за списком иди в **свой** reference: +[references/project-facts.md](../review-pipeline/references/project-facts.md) +конвейера ревью перечисляет все документы канона с их предметом. Пройди по этому +перечню — каждый документ получает строку, отрицание остаётся обязательным. +Триггеры при этом ты знаешь хуже, и это называется в докладе строкой: «синк +сделан по перечню документов, без списка триггеров — плагина `av-dev-pm` нет». Канона в проекте тоже нет — назови это исходом и предложи `av-dev-pm:canon`. ### 10. Коммит @@ -305,18 +312,18 @@ description: Автономно проводит одну задачу чере ### 11. Закрыть задачу — **после коммита, не раньше** **Вызови Skill `av-dev-pm:tasks`** и попроси закрыть задачу как реализованную — -он владеет форматом и двигает строку из `SPRINT.md` сам. Путь к его скрипту не +он владеет форматом и двигает строку из набора спринта сам. Путь к его скрипту не выясняй и индексы руками не правь: мост между плагинами — вызов скилла, а не путь. **Порядок обязателен.** Закрытие удаляет файл задачи; сделанное до коммита оно оставило бы задачу закрытой без единого следа работы, если шаг 10 упадёт. -**Закрытие тоже коммитится — вторым коммитом, тут же.** Удаление -`items/.md` и правка `SPRINT.md` — это правки в рабочем дереве, и оставить -их незакоммиченными нельзя по трём причинам: `task-batch` следом делает `rebase` +**Закрытие тоже коммитится — вторым коммитом, тут же.** Удаление файла задачи и +правка индексов (их имена знает `av-dev-pm`, не ты) — это правки в рабочем +дереве, и оставить их незакоммиченными нельзя по трём причинам: `task-batch` следом делает `rebase` и `worktree remove`, а те откажут на грязном дереве; закрытие, не доехавшее до -основной ветки, оставит задачу открытой молча; и опора «`SPRINT.md` под git +основной ветки, оставит задачу открытой молча; и опора «набор спринта под git показывает, что и когда закрыто» без коммита — пустые слова. Сообщение короткое, про учёт, а не про работу: `закрыта задача `. Это второй коммит осознанно: правило «одна задача — один осмысленный коммит» про работу, а учёт — не работа. diff --git a/av-dev-pm/skills/canon/references/changelog.md b/av-dev-pm/skills/canon/references/changelog.md index a7a80bc..70ca69c 100644 --- a/av-dev-pm/skills/canon/references/changelog.md +++ b/av-dev-pm/skills/canon/references/changelog.md @@ -45,3 +45,11 @@ upgrade` идёт по записям снизу вверх от версии п спринта. Убрать раздел «Процесс», если он пересказывает пайплайн. 13. В `openspec/config.yaml` оставить только нужды генерации и ссылки. 14. Добавить шаг `docs.py check` в гейт проекта. + +**Копии правил в шаблонах, которые версия 1 уносит в проект** — их правка в +каноне обязана появляться здесь отдельной версией: + +| Что копируется | Дом определения | +| --- | --- | +| форма записи журнала дефектов в `docs/review.md` | `av-dev-pipeline/skills/review-pipeline/references/review-journal.md` | +| правило заведения ADR в `docs/adr/README.md` | [canon.md](canon.md), раздел `adr/` | diff --git a/av-dev-pm/skills/canon/references/skeletons.md b/av-dev-pm/skills/canon/references/skeletons.md index 996fe56..f94b263 100644 --- a/av-dev-pm/skills/canon/references/skeletons.md +++ b/av-dev-pm/skills/canon/references/skeletons.md @@ -287,12 +287,14 @@ ## ГГГГ-ММ-ДД — краткое последствие [проскочил|пойман] -- **Где:** файл:строка -- **Симптом:** как обнаружилось -- **Чем воспроизведён:** тест, команда, замер -- **Почему не поймали:** только для проскочивших -- **Что меняем:** правило прохода, шаг гейта, конвенция — либо «ничего, цена - поимки выше цены дефекта» +- **Где:** путь:строка либо «конвейер, а не код» +- **Симптом:** как обнаружилось, кем и когда +- **Причина:** что на самом деле было не так +- **Чем воспроизведён:** тест, команда, замер — с числами +- **Почему не поймали:** только для проскочивших — какой проход обязан был найти + и что ему помешало +- **Что меняем:** правило прохода, шаг гейта, конвенция, факт в документе + проекта — либо «ничего, цена поимки выше цены дефекта» ``` Новый проект: «Дефектов пока не было. Настройка конвейера появится с первым diff --git a/av-dev-pm/skills/docs/SKILL.md b/av-dev-pm/skills/docs/SKILL.md index 621448b..4926376 100644 --- a/av-dev-pm/skills/docs/SKILL.md +++ b/av-dev-pm/skills/docs/SKILL.md @@ -102,8 +102,11 @@ description: Вести содержимое документов канона Файл держит два раздела с разными сроками жизни — журнал дефектов и настройку конвейера. **Что в каком и в какой форме — в [каноне](../canon/references/canon.md), раздел `review.md`**; подробности формы -записи и выбор адреса, куда она ведёт, — в конвейере ревью, -`references/review-journal.md`. +записи и выбор адреса, куда она ведёт, — у конвейера ревью проекта (при +`av-dev-pipeline` — `Skill av-dev-pipeline:review-pipeline`, его +`references/review-journal.md`). **Конвейера в проекте нет** — пиши по форме из +скелета `review.md`, которую положил канон, и скажи в докладе, что подробностей +формы взять негде. Твоя часть на синке: **дефект пишется сразу**, а не «потом, когда починим». Со временем теряется не факт, а причина непоймания — единственное, ради чего @@ -113,8 +116,11 @@ description: Вести содержимое документов канона ## Промоут в конвенции Находка → конвенция → правило линтера → **удаление из прозы**. Процедура целиком -принадлежит конвейеру ревью и живёт в его `references/promote.md`; роль каталога -конвенций — в [каноне](../canon/references/canon.md). +принадлежит конвейеру ревью проекта (при `av-dev-pipeline` — его +`references/promote.md`, читается через `Skill av-dev-pipeline:review-pipeline`); +роль каталога конвенций — в [каноне](../canon/references/canon.md). **Конвейера +нет** — три шага всё равно твои, просто без его процедуры: сформулируй правило, +поищи, чем оно механизируется, и вычеркни прозу, если механизировалось. Твоя часть — **третий шаг, который пропускают чаще всего**: правило заработало, а формулировка осталась в прозе, и проход продолжает проверять уже проверенное. diff --git a/av-dev-pm/skills/session/SKILL.md b/av-dev-pm/skills/session/SKILL.md index 7762d39..a6d12ef 100644 --- a/av-dev-pm/skills/session/SKILL.md +++ b/av-dev-pm/skills/session/SKILL.md @@ -168,10 +168,13 @@ python3 $tk reopen <слаг> --dir D --reason … # приёмка не со пайплайн физически не мог. Теперь мост есть, и защита у трёх обходов ниже — **только текстовая**. Опоры, которые остались настоящими: -- **отчёт триажа** в `openspec/changes/archive//review/` (до архивации — - `changes//review/`) — независимый артефакт, - написанный ревью, а не исполнителем; по нему сверяют состав прогона и урожай; -- **`SPRINT.md` под git** — `git log -p` показывает, что и когда было закрыто; +- **независимый отчёт ревью** — артефакт, написанный не исполнителем; по нему + сверяют состав прогона и урожай. Где он лежит, знает пайплайн проекта; при + конвейере `av-dev-pipeline` это отчёт триажа в + `openspec/changes/archive//review/` (до архивации — `changes//review/`); +- **`SPRINT.md` под git** — `git log -p` показывает, что и когда было закрыто. + Работает, только если закрытие **закоммичено**: удаление файла задачи и правка + индекса, оставшиеся в рабочем дереве, никакой истории не образуют; - **`reopen <слаг> --reason`** — закрытие не окончательно. Приёмка человеком на сессии его отменяет, и это штатная операция, а не скандал. @@ -191,10 +194,18 @@ python3 $tk reopen <слаг> --dir D --reason … # приёмка не со Пол для остатка — польза, названная в хуке; проверяет его человек при приёмке, и `reopen` — его инструмент. - **Занизить урожай** — не заводить найденное по ходу. Защита: поимённая сверка - со **сохранённым отчётом триажа**, а не с прозой исполнителя. Каждая + с **сохранённым независимым отчётом**, а не с прозой исполнителя. Каждая отложенная находка имеет либо слаг, либо строку «не заведена: причина». Нулевой урожай при непустом отчёте виден сразу. +**Проект без конвейера ревью — независимого отчёта нет, и это надо сказать, а не +обойти молча.** Задачи делались руками или чужим пайплайном, сверять урожай не с +чем: остаётся проза исполнителя, то есть тот же взгляд, что и у автора. Тогда +защита от занижения урожая **снята**, и доклад спринта обязан нести строку «урожай +сверялся с отчётом исполнителя — независимого отчёта в проекте нет». Дальше это +решение человека: завести конвейер, принимать выборочной перепроверкой или +согласиться с ценой. Молчание здесь хуже любого из трёх исходов. + Стимулы внутри пайплайна задачи (занизить требования к проверке, пропустить проход) принадлежат ему и защищены там же. diff --git a/av-dev-pm/skills/session/references/sprint.md b/av-dev-pm/skills/session/references/sprint.md index b000f05..bc88790 100644 --- a/av-dev-pm/skills/session/references/sprint.md +++ b/av-dev-pm/skills/session/references/sprint.md @@ -87,6 +87,12 @@ упавший коммит после закрытия оставил бы задачу закрытой без единого следа работы. +**Само закрытие тоже коммитится, отдельным коммитом.** Удаление файла задачи и +правка индекса — правки в рабочем дереве; пока они не в истории, `SPRINT.md` +ничего не показывает, а `reopen` восстанавливает текст из `HEAD` в узком окне, +которое закончится первым посторонним коммитом. Сообщение про учёт, а не про +работу: `закрыта задача `. + **Дорога назад существует и обязана быть названа.** Человек на сессии сверил критерии, и приёмка не сошлась — `tasks.py reopen --reason "приёмка не сошлась: …"`: @@ -106,11 +112,13 @@ сам. 2. **Принимает человек на сессии, а не отдельный агент.** Декорреляция исполнителя и приёмщика в момент закрытия **снята** (решение о снятии и его - цена — в `SKILL.md`, «Стимулы»). Опоры остались три: сохранённый отчёт триажа - в `openspec/changes/archive//review/` (до архивации — `changes//review/`) - — независимый артефакт, `SPRINT.md` под git - и `reopen`. Переоценка на сессии и есть момент, когда критерии видит не - исполнитель. + цена — в `SKILL.md`, «Стимулы»). Опоры остались три: **сохранённый независимый + отчёт ревью** (при конвейере `av-dev-pipeline` — отчёт триажа в + `openspec/changes/archive//review/`, до архивации — `changes//review/`), + `SPRINT.md` под git и `reopen`. Переоценка на сессии и есть момент, когда + критерии видит не исполнитель. **Конвейера ревью в проекте нет — первой опоры + нет тоже**, и это называется строкой доклада, а не обходится молча + (`SKILL.md`, «Стимулы»). 3. **Расхождение — дефект критериев.** Приёмщик правит критерии и возвращает задачу исполнителю **в этом же спринте**: ответ есть, остаток есть, по тесту про остаток это не выход из спринта. @@ -135,9 +143,11 @@ - **Какие развилки решались** и чем обоснованы. - **Урожай:** сколько задач заведено, какие вопросы накопились, что вышло из спринта и почему, что было внеплановым. -- **Поимённая сверка урожая** с отчётами ревью: каждая отложенная находка имеет - либо слаг, либо строку «не заведена: причина». Нулевой урожай при непустом - отчёте — сигнал, а не благополучие. +- **Поимённая сверка урожая** с независимыми отчётами ревью: каждая отложенная + находка имеет либо слаг, либо строку «не заведена: причина». Нулевой урожай при + непустом отчёте — сигнал, а не благополучие. **Отчётов нет** (проект без + конвейера ревью) — сверять не с чем, и строка доклада говорит именно это, а не + «сверено». - **Созрела ли порция для сессии.** Решение звать — человека, напоминание — обязанность агента: `⌈урожай / 8⌉` порций. - **Границы покрытия** сжатой строкой: что в этом спринте не проверялось вовсе.