diff --git a/DECISIONS.md b/DECISIONS.md index 8872c07..c26c440 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -2084,3 +2084,124 @@ dev-skills — **маркетплейс плагинов**: скилл комм реестром, манифест ему не нужен. Правило записано после проверки, а не из осторожности, — и осторожность здесь стоила бы лишнего абзаца в README про починку, которой не бывает. + +## 31. Ревизия покрытия `av-dev-pm` продакт-оптикой (2026-08-05) + +Сабагент в роли продакт-менеджера оценил покрытие жизненного цикла личного +проекта (один человек, недели-месяцы) скиллами и агентами `av-dev-pm`. Скоуп +сужен по ходу разбора: деплой и разбор инцидентов на проде делаются вручную, +скиллов под них не заводим. Осталось планирование, разработка и доработка. + +**ААББЕЕ. Шаг 2 сессии требовал чисел, которых процесс отказался собирать +решением.** `cadence.md` делал обязанностью пересмотр «ориентира по размеру +спринта, прироста беклога на закрытую задачу, времени на задачу» и «сколько +заняли задачи **против ожидания**» — с обоснованием «иначе обязанность висит +ничья». Данных под это нет: у записи нет дат заведения, взятия и закрытия, +`close --implemented` удаляет файл, `sprint close` очищает `SPRINT.md`. Хуже +того, «против ожидания» и «время на задачу» требуют оценки и тайм-бокса, а +`session/SKILL.md` в «Почему не Scrum» их прямо не берёт: пункт противоречил +решению, стоящему через файл от него. + +Исход — **выкинуть, а не подпереть данными**. На практике числа не +пересматривались ни разу, и заводить под них учёт дат значило бы обслуживать +обязанность, которой никто не брал. Осталось качественное: что сломалось в +процессе, что оказалось дороже, чем выглядело при заведении, какие правила не +сработали. Шаг 3 пункт 9 переименован из «переоценки по измеренному» в +«переоценку по пройденному», судит человек по памяти о спринте. Рядом записано, +что замеров нет **намеренно** — иначе следующий читатель заведёт их обратно как +недостающие. + +**ААББЖЖ. `doc-consistency` переехал с каждого синка на сессию, к +`doc-code-drift`.** Агент на `opus` зовётся шагом 9 пайплайна, то есть на каждой +задаче: 5–8 opus-проходов за спринт по документам, которые за спринт меняются на +несколько абзацев. Обоснование в каноне («сверка текста с текстом дёшева») верно +относительно второго агента, но не в абсолюте на одиночке. + +Довод сильнее денег: **расхождение между двумя документами по определению +требует двух документов**, а на большинстве задач синк правит один. И пачка, +отбираемая работой, не видит того, чего работа не касалась, — а расхождение живёт +ровно там: правка отменяет решение в одном документе, парный статус нужен в +другом. Это был открытый вопрос `REMAINING` про охват ADR при пересмотре; переезд +его закрыл. Цена — потеря привязки находки к задаче, которая её породила: по теме +29 именно эта привязка дала пять самых точных находок. Принято сознательно. + +**ААББЗЗ. Отмена цели получила порядок, но не флаг.** `close` запрещал закрыть +цель с живыми задачами, а что делать с этими задачами, не говорил нигде: шаг 3 +сессии знал только «та ли цель», `task-goal.md` описывал одно достижение, а +`session/SKILL.md` вдобавок утверждал «цель постоянна». Человек получал отказ с +перечнем и никакой подсказки. + +Порядок записан: сперва задачи поштучно (`close --reason` своей причиной либо +`edit --goal` на другую цель), потом сама цель через `close --reason` в +`REJECTED.md`, а не в `Готово` — отменённая цель не умеет ничего. Флаг +`--cascade` отвергнут: отмена цели редка и дорога, и поштучный разбор здесь не +церемония, а единственный момент, когда видно, что из задач переживёт цель. +Каскад превратил бы его в один Enter. **Причина у каждой задачи своя**: «цель +отменена» это пересказ команды, в `REJECTED.md` от него нет пользы через квартал. + +Место процедуры — переоценка на сессии, а не отдельный заход: отмена цели **и +есть** разбор всех её задач, а разбор задач — шаг 3. + +**ААББИИ. У брошенного спринта появился второй законный исход, без порога.** +`--dissolve` во всех текстах был привязан к блокеру, и скрипт отказывал словами +«роспуск объясняется блокером». Вернувшийся к набору, который стоял месяц, не +имел законного хода: двигать нельзя (заморозка), распускать не по чему. Теперь +роспуск объясняется блокером **или тем, что набор протух**. + +Порога в неделях сознательно нет — это тот же класс, что выкинутые числа шага 2: +счётчик простоя пришлось бы вести руками, а решает всё равно человек. Признак не +срок, а **что набор перестал быть твоим**: перечитываешь, зачем эти задачи вместе +— он протух. Туда же добавлена точка входа «вернулся, а спринт открыт»: `check`, +`SPRINT.md`, развилка продолжать/распустить. Середины у развилки нет намеренно — +«доделаю пару штук и решу» это работа по набору, которого ты не понимаешь. + +**ААББКК. Журнал канона прогоняется как есть, а проверка исхода поручена +судьям.** Схлопнуть записи 3 и 4 в один переход «с 2 на 4» отвергнуто: журнал +описывает не только *что сделать*, но и порядок, в котором это делалось, и слитая +запись экономит один проход ценой невоспроизводимости остальных. Оба живых +проекта пройдут 2→3→4 по записям. + +Взамен появилась проверка исхода: **шагом 6 `adopt` и шагом 6 `upgrade` зовутся +оба судьи документов**. Это прямой ответ на открытый вопрос REMAINING «как +проверять, что канон не разошёлся с проектами после `upgrade`»: `check` сверяет +**число** в `.pm.json` с версией скрипта и про существо записи не знает ничего. +Проект несёт `"canon": 4` и может не иметь того, чего требовала любая из +пройденных версий — записи применяются руками, а ручной проход по трём записям +подряд ровно то место, где половина шага делается и забывается. + +У `adopt` добавка другого рода: там судьи ловят не недоделанную миграцию, а +последствия переноса — факт, растащенный по двум домам, поведение, осевшее в +`architecture.md`, ADR, оторванный от своего `design.md`. Им передаётся +объявленное переходное состояние из шага 5, иначе честная строка в незаполненном +слоте вернётся находкой. + +### Что из этого следует + +117. **Обязанность без источника данных отменяют, а не механизируют.** Первый + позыв — дать шагу данные (дописать даты, сводку спринта). Но обязанность, + не исполнявшуюся ни разу, дешевле снять: механизация под неё производит + учёт, который надо вести, ради разбора, который не делается. +118. **Требование, противоречащее решению через файл от него, — не мелочь, а + признак копии.** «Против ожидания» пережило решение «не берём оценки», + потому что стояло в другом документе. Обратный обход по решению «что мы не + берём» нашёл бы это сразу — тот же приём, что и следствие 109. +119. **Частота вызова агента выводится из того, что он ищет.** Судья + расхождений **между** документами бессмысленен там, где документ один; + значит его место не на задаче, а на наборе задач. Цена вызова подтвердила + вывод, но не она его дала. +120. **Запрет обязан называть выход.** `close` верно не давал осиротить задачи, + но текст отказа перечислял препятствия и молчал о ходе. Проверка без + названного следующего шага — половина работы: она защищает данные и бросает + человека. +121. **Признак вместо порога там, где счётчик пришлось бы вести руками.** + «Набор перестал быть твоим» проверяется в момент вопроса и ничего не + требует хранить; «прошло N недель» требует учёта, который никто не ведёт, и + всё равно кончается решением человека. +122. **Версионирование без единого переехавшего проекта — не журнал миграций, а + история правок.** Довод за схлопывание был верен по факту и отвергнут по + принципу: обкатка на живых проектах и проверяет, работает ли механизм. + Схлопнуть значило бы не прогнать его ни разу и оставить вопрос открытым. +123. **Проверка версии не есть проверка миграции.** Число в `.pm.json` двигает + тот же проход, что делал шаги, — и двигает независимо от того, все ли + сделаны. Механической проверки существа нет; там, где её нет, ставится + судья, а не отметка. diff --git a/REMAINING.md b/REMAINING.md index 7f58b3c..8857901 100644 --- a/REMAINING.md +++ b/REMAINING.md @@ -40,6 +40,10 @@ severity. Пробы готовы и синтетических не нужно перед переездом jellybit и блокирует его (решение 39), а ожидаемый исход уже назван выше. +**«Главный» здесь про цену, а не про очередь.** Первой идёт адаптация healthlog +(TODO, раздел 2): без неё нет проекта под каноном, на котором работают остальные +скиллы. Калибровка блокирует один шаг — переезд jellybit, — а не всё подряд. + ## Что ещё не сделано Список работ — в [TODO.md](TODO.md). Здесь только то, что стоит держать в голове @@ -66,12 +70,28 @@ severity. Пробы готовы и синтетических не нужно **Как проверять, что канон не разошёлся с проектами после `upgrade`.** `canon check` сверяет версию, но не то, что миграционные записи journal'а применены верно. Проект может нести `"canon": 2` и не иметь того, что версия 2 требовала. +Ответ выбран: шагом 6 `upgrade` зовутся оба судьи документов — проверка не +механическая, но других у существа записей нет. Останется открытым, пока не +прогнано на живом проекте: неизвестно, ловят ли они недоделанную миграцию или +только её последствия. + +**Не выродились ли «границы покрытия» в шаблон.** Строка «что смотрели и чего не +смотрели» обязательна в докладе `check`, `adopt`, интейка, штурма, сессии, +спринта и всех четырёх агентов — семь и больше раз за сессию, и проверить её +исполнение некому: приёмщик и исполнитель одно лицо (`session/SKILL.md`, +«Стимулы»). Выродившаяся строка **хуже отсутствия**: доклад выглядит проверенным. + +Приём не правится: это гипотеза об износе, а не находка, и менять работающее по +догадке дороже. **Наблюдение к первой обкатке на живом проекте:** если в трёх +докладах подряд границы покрытия совпали дословно или называют не то, чего +проверка действительно не касалась, — приём выродился, и вот тогда решать. **Форма ADR при пересмотре решения.** Парный статус («старая запись получает -`заменено на`») судит агент `doc-consistency` — правило 6 его устава. Открытым -остаётся не это, а охват: агент зовётся на синке по документам, которых синк -касался, и пересмотр, отменяющий решение из документа, к которому не -притрагивались, он не увидит. Механической проверки по-прежнему нет. +`заменено на`») судит агент `doc-consistency` — правило 6 его устава. Охват был +открытым вопросом, пока агент зовётся пачкой, отобранной работой; переезд вызова +на сессию с пачкой «весь канон» его снял. Остаётся зазор в спринт и отсутствие +механической проверки — то есть пересмотр, сделанный сегодня, судится на +ближайшей сессии, а не в момент правки. ## Известные пределы — приняты, чинить не планируется diff --git a/av-dev-pm/agents/doc-code-drift.md b/av-dev-pm/agents/doc-code-drift.md index cab9757..d0dba7a 100644 --- a/av-dev-pm/agents/doc-code-drift.md +++ b/av-dev-pm/agents/doc-code-drift.md @@ -1,6 +1,6 @@ --- name: doc-code-drift -description: "Сверка документов канона с кодом по закрытому перечню проверяемых фактов: имя основной ветки и команды из CLAUDE.md, запреты с путями, testdata и временный каталог, путь миграций из .pm.json, внешние зависимости поимённо в architecture.md против манифеста, настройки с числовым значением в database.md против конфига и кода, единые точки проекта против реального числа реализаций, capability против существующих модулей. Отвечает на «этот факт ещё верен», а не «эта архитектура правильная». Читает весь репозиторий, гоняет только читающие команды. Отдаёт готовые формулировки и ничего не правит сам. Согласованность документов между собой смотрит агент doc-consistency. Использовать на сессии между спринтами и перед приведением проекта к канону. Только чтение." +description: "Сверка документов канона с кодом по закрытому перечню проверяемых фактов: имя основной ветки и команды из CLAUDE.md, запреты с путями, testdata и временный каталог, путь миграций из .pm.json, внешние зависимости поимённо в architecture.md против манифеста, настройки с числовым значением в database.md против конфига и кода, единые точки проекта против реального числа реализаций, capability против существующих модулей. Отвечает на «этот факт ещё верен», а не «эта архитектура правильная». Читает весь репозиторий, гоняет только читающие команды. Отдаёт готовые формулировки и ничего не правит сам. Согласованность документов между собой смотрит агент doc-consistency. Использовать на сессии между спринтами, а также после приведения проекта к канону (adopt) и после повышения версии канона (upgrade). Только чтение." tools: Read, Grep, Glob, Bash model: fable color: red diff --git a/av-dev-pm/agents/doc-consistency.md b/av-dev-pm/agents/doc-consistency.md index b1460c4..1432f08 100644 --- a/av-dev-pm/agents/doc-consistency.md +++ b/av-dev-pm/agents/doc-consistency.md @@ -1,6 +1,6 @@ --- name: doc-consistency -description: "Сверка документов канона между собой и с openspec: один факт, живущий в двух домах, прямое противоречие между документами (периметр, зависимости, обратимость), поведение системы, осевшее в architecture.md вместо спек, capability без обзора или с пересказом требований, число без провенанса в research, ADR без ссылки на архивный design.md и без парного статуса при замене, заглушка вместо честной строки в пустом слоте. Читает docs/ и openspec/, кода не читает. Отдаёт готовые формулировки и ничего не правит сам. Соответствие документов коду смотрит агент doc-code-drift, язык — doc-wording. Использовать на шаге синка документации и перед приведением проекта к канону. Только чтение." +description: "Сверка документов канона между собой и с openspec: один факт, живущий в двух домах, прямое противоречие между документами (периметр, зависимости, обратимость), поведение системы, осевшее в architecture.md вместо спек, capability без обзора или с пересказом требований, число без провенанса в research, ADR без ссылки на архивный design.md и без парного статуса при замене, заглушка вместо честной строки в пустом слоте. Читает docs/ и openspec/, кода не читает. Отдаёт готовые формулировки и ничего не правит сам. Соответствие документов коду смотрит агент doc-code-drift, язык — doc-wording. Использовать на сессии между спринтами, а также после приведения проекта к канону (adopt) и после повышения версии канона (upgrade), на весь канон разом; на отдельной задаче не звать. Только чтение." tools: Read, Grep, Glob model: opus color: yellow diff --git a/av-dev-pm/skills/canon/SKILL.md b/av-dev-pm/skills/canon/SKILL.md index 538012e..8433040 100644 --- a/av-dev-pm/skills/canon/SKILL.md +++ b/av-dev-pm/skills/canon/SKILL.md @@ -82,14 +82,14 @@ capability: незаполненный канон это переходное с ## `check` 1. `docs.py check`, при наличии базы диффа — с `--base`. -2. **Позови `doc-consistency`** на документы, которых касалась работа. Не «заодно - по всему `docs/`»: агент зовётся пачкой, но пачка отбирается работой. -3. **`doc-code-drift`** — не на каждом `check`, а перед приведением проекта к - канону и раз в спринт (шаг сессии). Он дорог: читает репозиторий и гоняет - команды. Позвал — передай ему раздел запретов `CLAUDE.md`. -4. Доклад: вывод скрипта строкой исхода, находки агентов поимённо, **граница - покрытия** — что смотрели и чего не смотрели, и **был ли позван - `doc-code-drift`**: доклад, умолчавший об этом, читается как «с кодом сверено». +2. **Агентов на каждом `check` не зови.** Оба — `doc-consistency` и + `doc-code-drift` — зовутся раз в спринт (шаг сессии), а также шагом 6 `adopt` + и шагом 6 `upgrade`, на весь канон разом. Они дороги: оба на `opus`, второй + ещё и читает репозиторий. Позвал `doc-code-drift` — передай ему раздел + запретов `CLAUDE.md`. +3. Доклад: вывод скрипта строкой исхода, находки агентов поимённо, **граница + покрытия** — что смотрели и чего не смотрели, и **кто из двоих был позван**: + доклад, умолчавший об этом, читается как «сверено». Дрейф раскладки чинится переносом; смысловые находки — это либо правка документа, либо задача, если работы больше чем на абзац. @@ -167,6 +167,20 @@ capability), `openspec/config.yaml`. содержания, сколько маркеров долга в `architecture.md`, сколько задач без критериев приёмки. Закрывается порциями по ходу работы, а не одним заходом. +### 6. Позови обоих судей + +`docs.py` увидел раскладку, а не смысл: перенос растащил один факт по двум домам, +оставил в `architecture.md` поведение, которому место в спеке, и оторвал ADR от +его `design.md`. Ничего из этого скрипт не видит, и первый прогон на живом +проекте обычно самый урожайный — правило единственного дома до адаптации никто не +проверял. + +Зови **`doc-consistency`** (документы между собой и с openspec) и +**`doc-code-drift`** (факты против кода). Разбирай порциями, а не одним заходом. + +**Передай им объявленное переходное состояние из шага 5** — иначе честная строка +в незаполненном слоте вернётся находкой, а это не поломка, а объявленный долг. + ## `upgrade` — канон вырос 1. `docs.py version` — версия проекта и версия скрипта. @@ -177,10 +191,21 @@ capability), `openspec/config.yaml`. применяются по порядку. 4. Подними `canon` в `docs/.pm.json` до текущей. 5. `docs.py check`. +6. **Позови обоих судей** — `doc-consistency` и `doc-code-drift`. Записи журнала описывают **что сделать проекту**. Если запись этого не говорит — это дефект журнала, и о нём надо сказать, а не догадываться. +**Шаг 6 обязателен, и вот почему.** `check` сверяет **число** в `.pm.json` с +версией скрипта — и только его. Применена ли запись журнала **по существу**, он +не знает: проект несёт `"canon": 4` и может не иметь того, чего требовала любая +из пройденных версий. Записи применяются руками (переименовать секцию, проставить +типы, дописать раздел каждому `fix`), а ручной проход по нескольким записям +подряд — ровно то место, где половина шага делается и забывается. Судьи и есть +проверка, которой у `upgrade` иначе нет: `doc-consistency` увидит, что документы +разошлись после переименований, `doc-code-drift` — что переехавший факт +разошёлся с кодом. + ## Чего этот скилл не делает - **Не сочиняет содержание.** Пустой слот получает честную строку о том, что его diff --git a/av-dev-pm/skills/canon/references/canon.md b/av-dev-pm/skills/canon/references/canon.md index abd7d60..34cbf57 100644 --- a/av-dev-pm/skills/canon/references/canon.md +++ b/av-dev-pm/skills/canon/references/canon.md @@ -390,12 +390,18 @@ kebab-case.** Причина не эстетическая: имя файла с | | связность и читаемость | `doc-wording` | **Агентов двое, и разведены они по глубине, а не по охвату.** `doc-consistency` -читает только `docs/` и `openspec/` — сверка текста с текстом дёшева и зовётся на -каждом синке документации. `doc-code-drift` читает репозиторий и гоняет читающие -команды: дорого, и зовётся раз в спринт и перед приведением проекта к канону. -Слитый агент делал бы дешёвую половину редкой, а дорогую — поверхностной; тот же +читает только `docs/` и `openspec/`, `doc-code-drift` — весь репозиторий и гоняет +читающие команды. Слитый агент делал бы одну половину поверхностной; тот же разрез, что между `task-form` и `doc-wording`. +**Зовутся оба одинаково — раз в спринт на сессии, а также после `adopt` и после +`upgrade`, на весь канон разом.** Не на синке документации: агент на `opus` по +каждой сделанной задаче не окупается, а расхождение между двумя документами по +определению требует двух, и на большинстве задач синк правит один. Пачка, +отбираемая работой, вдобавок не видит того, чего работа не касалась, — а именно +там расхождение и живёт: правка отменяет решение в одном документе, парный статус +нужен в другом. + **Перечень фактов, которые `doc-code-drift` сверяет с кодом, закрыт** — имя основной ветки, команды, пути, зависимости поимённо, настройки с числовым значением, единые точки проекта, capability, проверяемые инварианты. «Сверить diff --git a/av-dev-pm/skills/canon/references/changelog.md b/av-dev-pm/skills/canon/references/changelog.md index ae40de8..73fe5f1 100644 --- a/av-dev-pm/skills/canon/references/changelog.md +++ b/av-dev-pm/skills/canon/references/changelog.md @@ -92,9 +92,9 @@ upgrade` идёт по записям снизу вверх от версии п существовало. Судьи заведены и разведены по глубине: **`doc-consistency`** (документ ↔ документ ↔ openspec: факт в двух домах, прямое противоречие, поведение в `architecture.md` вместо спек, ADR без ссылки и парного статуса, - число без провенанса, заглушка вместо честной строки) зовётся на шаге синка - документации; **`doc-code-drift`** (документ ↔ код по закрытому перечню - фактов) — раз в спринт на сессии и перед приведением проекта к канону. + число без провенанса, заглушка вместо честной строки); **`doc-code-drift`** + (документ ↔ код по закрытому перечню фактов). Оба зовутся раз в спринт на + сессии, а также после adopt и после upgrade, на весь канон разом. **Что сделать проекту:** @@ -127,10 +127,13 @@ upgrade` идёт по записям снизу вверх от версии п человека. **Переименование ADR это перенос ссылок**: слаг стоит в `adr/README.md`, в `architecture.md` и в чужих документах, и делается одним проходом, иначе останутся битые ссылки (их `docs.py` потом и покажет). -8. Позвать `doc-consistency` на документы канона — первый прогон на живом - проекте обычно самый урожайный: правило единственного дома до сих пор никто - не проверял. Разбирать порциями, а не одним заходом. -9. `docs/.pm.json`: `"canon": 4`. +8. `docs/.pm.json`: `"canon": 4`. +9. Позвать **обоих судей** — `doc-consistency` и `doc-code-drift`, шагом 6 + `upgrade`. Пунктов выше девять, половина из них ручная, и именно здесь видно, + какие сделаны только наполовину: переименования секций и полей разводят + документы, а `check` сверяет число версии, а не существо. Первый прогон на + живом проекте вдобавок самый урожайный — правило единственного дома до сих пор + никто не проверял. Разбирать порциями, а не одним заходом. ## Версия 3 — 2026-08-04 diff --git a/av-dev-pm/skills/docs/SKILL.md b/av-dev-pm/skills/docs/SKILL.md index e7147ef..adfc5f5 100644 --- a/av-dev-pm/skills/docs/SKILL.md +++ b/av-dev-pm/skills/docs/SKILL.md @@ -53,25 +53,25 @@ description: Вести содержимое документов канона - adr/ — заведён ADR-2026-08-03-queue-as-table: отказ от внешней очереди - research/ — новое о формате не узнано - passport, security, conventions, review — не требуется: изменение внутреннее -- сверка doc-consistency: находок нет, просмотрено 4 документа из 10 ``` -## Сверка после синка +## Сверка — не здесь, а на сессии Синк правит документы поодиночке, а расходятся они **между собой**: факт, дописанный в `architecture.md`, уже живёт в `CLAUDE.md`; периметр в -`security.md` не знает про новый эндпоинт. Поймать это на своей же правке нельзя -— поэтому последним шагом синка зовётся агент **`doc-consistency`** на те -документы, которых синк касался. +`security.md` не знает про новый эндпоинт. Поймать это на своей же правке нельзя, +и судит это агент `doc-consistency`. -Он читает `docs/` и `openspec/`, кода не читает, ничего не правит и возвращает -готовые формулировки. Строка его доклада входит в доклад синка — **включая -пустую**: «находок нет, просмотрено N из M» это ответ, а молчание читается как -«не звали». +**Но синк его не зовёт.** Оба судьи документов — `doc-consistency` и +`doc-code-drift` — зовутся раз в спринт, шагом сессии, на весь канон разом. +Причина в цене: агент на `opus` по каждой сделанной задаче — самая дорогая +церемония процесса, а расхождение между двумя документами по определению требует +двух документов, и на большинстве задач синк правит один. -**Сверку с кодом синк не зовёт.** «Протухший факт, разошедшийся с кодом» смотрит -`doc-code-drift`, он дорог (читает репозиторий) и зовётся раз в спринт на сессии -— не на каждой сделанной задаче. +Что теряется: привязка находки к задаче, которая её породила. Что выигрывается, +кроме денег: пачка перестаёт отбираться синком, и в неё попадают документы, +которых работа не касалась, — расхождение, внесённое правкой в одном месте, там +и живёт. ## ADR — промоут, а не второе сочинение diff --git a/av-dev-pm/skills/session/SKILL.md b/av-dev-pm/skills/session/SKILL.md index 11e2089..3f22529 100644 --- a/av-dev-pm/skills/session/SKILL.md +++ b/av-dev-pm/skills/session/SKILL.md @@ -1,6 +1,6 @@ --- name: session -description: "Ритуал между спринтами и ведение самого спринта: разбор накопившихся вопросов, разбор прошедшего спринта про процесс, переоценка задач порциями, выбор цели и набор нового спринта с заморозкой. Плюс правила по ходу спринта — что врывается в замороженный набор, чем вопрос отличается от блокера, когда задача выходит из спринта, что считается сделанным и что идёт в доклад. Использовать, когда просят закрыть или начать спринт, собрать набор, разобрать вопросы, провести груминг/переоценку/ретроспективу, решить «что делать дальше» или доложить итоги. Формат и содержимое задач — скилл tasks." +description: "Ритуал между спринтами и ведение самого спринта: разбор накопившихся вопросов, разбор прошедшего спринта про процесс, переоценка задач порциями, выбор цели и набор нового спринта с заморозкой. Плюс правила по ходу спринта — что врывается в замороженный набор, чем вопрос отличается от блокера, когда задача выходит из спринта, что считается сделанным и что идёт в доклад. Использовать, когда просят закрыть или начать спринт, собрать набор, разобрать вопросы, провести груминг/переоценку/ретроспективу, решить «что делать дальше» или доложить итоги, а также когда вернулись к проекту после перерыва и надо понять, где остановились. Формат и содержимое задач — скилл tasks." --- # Сессия между спринтами @@ -38,7 +38,9 @@ description: "Ритуал между спринтами и ведение са ## Единицы - **Цель** — то, ради чего набирается спринт. Файл типа `goal` (🎯), - перечисленный в `ROADMAP.md`. Цель постоянна: живёт, пока живёт направление. + перечисленный в `ROADMAP.md`. Цель постоянна: живёт, пока живёт направление, — + и уходит вместе с ним, если замысел оказался неверен (порядок отмены — в + [tasks](../tasks/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель)). - **Задача** — то, что мерджится целиком и даёт видимую пользу. - **Вопрос** — решение человека. Не останавливает начатую работу, но **блокирует взятие** задачи в спринт. Живёт внутри файла задачи разделом «Вопросы» и тегом @@ -116,8 +118,9 @@ description: "Ритуал между спринтами и ведение са Это зависимость, а не список. 1. **Разбор вопросов.** -2. **Разбор прошедшего спринта — про процесс, а не про задачи.** Здесь же - сверка документов канона с кодом — агент `doc-code-drift`, раз в спринт. +2. **Разбор прошедшего спринта — про процесс, а не про задачи.** Здесь же оба + судьи документов канона на весь канон разом, раз в спринт: `doc-consistency` + (документы между собой) и `doc-code-drift` (документы против кода). 3. **Переоценка задач** порциями. 4. **Выбор цели и набор спринта.** Цель называет человек, набор собирает агент и показывает **до старта работ**. @@ -144,6 +147,25 @@ flowchart TD Схема — **сводка**: процедура каждого шага в [references/cadence.md](references/cadence.md), и при расхождении прав текст. +## Вернулся, а спринт открыт + +Сессия — ритуал **между** спринтами, и шаг 1 предполагает только что закрытый. +Вход после перерыва другой, и начинается он не с шага, а с вопроса, свой ли ещё +набор: + +1. `tasks.py check` — блок здоровья скажет состояние спринта, число готовых к + взятию и залежавшихся; при расхождении раскладки `--fix`. +2. Прочитать `SPRINT.md`: цель, состав, дата начала. +3. **Развилка, и решает её человек.** Набор всё ещё твой — продолжай спринт, ни + сессии, ни переоценки не нужно, они между спринтами. Перечитываешь, зачем эти + задачи собраны вместе, — набор протух: `sprint close --dissolve --reason …`, + недоделанное возвращается в беклог, дальше обычная сессия с шага 1. + +Порога в неделях нет намеренно — почему, в +[references/sprint.md](references/sprint.md), «Протухший набор». +Середины у развилки тоже нет: «доделаю пару штук и решу» — это работа по набору, +которого ты уже не понимаешь. + Процедура каждого шага, размер и отбор порции, храповик на залежавшихся, формат интерактива и доклад — [references/cadence.md](references/cadence.md). diff --git a/av-dev-pm/skills/session/references/cadence.md b/av-dev-pm/skills/session/references/cadence.md index e7ebe24..1329eed 100644 --- a/av-dev-pm/skills/session/references/cadence.md +++ b/av-dev-pm/skills/session/references/cadence.md @@ -37,13 +37,15 @@ Не «что мы сделали» (это доклад спринта, он уже был), а: - **что сломалось в процессе и почему не поймали** — промах, доехавший до конца; -- **сколько на самом деле заняли задачи** против ожидания; +- **что оказалось дороже, чем выглядело при заведении** — не число, а сам факт и + причина: чего не было видно в постановке; - **какие правила не сработали или сработали не так** — в том числе правила - этого плагина; -- **какие числа пора пересмотреть** — ориентир по размеру спринта, прирост - беклога на одну закрытую задачу, время на задачу. Эта обязанность иначе висит - ничья: числа, помеченные как «первый замер», не пересматриваются никогда, если - их не пересматривает конкретный шаг. + этого плагина. + +Замеров процесс не ведёт намеренно: оценки в очках и velocity не взяты +(«[Почему не Scrum](../SKILL.md#почему-не-scrum)»), а спринт ограничен объёмом, а +не временем — сравнивать «сколько заняло» не с чем. Разбор здесь качественный, и +это не упущение. **Артефакт обязателен.** Вывод, оставшийся в контексте сессии, не существует: следующая сессия его не увидит. Дом у него один и известен из канона — @@ -54,20 +56,32 @@ Отдельным ритуалом ретроспектива не выделяется: процесс личный, синхронизировать некого. -**Здесь же зовётся `doc-code-drift`** — сверка документов канона с кодом по -закрытому перечню фактов: имя основной ветки, команды, пути, внешние зависимости -поимённо, настройки с числовым значением, единые точки проекта, capability. +**Здесь же зовутся оба судьи документов** — на весь канон разом, а не на пачку, +отобранную работой: -Раз в спринт, а не чаще, и причина в цене: агент читает репозиторий и гоняет -читающие команды. Но и не реже — **спринт это ровно то, что двигает код под -документами**: переименованная цель сборки, ушедшая зависимость, второй способ -делать то, что обзор объявил единственным. Протухший факт неотличим от свежего, и -по нему принимают решения, пока кто-нибудь не наткнётся. +- **`doc-consistency`** — согласованность документов между собой и с openspec: + факт в двух домах, прямое противоречие, поведение в `architecture.md` вместо + спек, ADR без парного статуса при замене, число без провенанса; +- **`doc-code-drift`** — сверка с кодом по закрытому перечню фактов: имя основной + ветки, команды, пути, внешние зависимости поимённо, настройки с числовым + значением, единые точки проекта, capability. -Его находки — обычный материал переоценки: строка на замену идёт в документ сразу, -работа больше чем на абзац становится задачей типа `chore`. **Позвал — скажи в -докладе, что позвал, и приложи его таблицу проверенного**; не позвал — скажи и -это, иначе доклад читается как «с кодом сверено». +Раз в спринт, а не чаще, и причина в цене: оба на `opus`, а второй ещё и читает +репозиторий. Но и не реже — **спринт это ровно то, что двигает код и документы**: +переименованная цель сборки, ушедшая зависимость, второй способ делать то, что +обзор объявил единственным; факт, дописанный в `architecture.md`, уже живущий в +`CLAUDE.md`. Протухшее и раздвоившееся неотличимо от свежего, и по нему принимают +решения, пока кто-нибудь не наткнётся. + +**Пачка — весь канон, и это не расточительство, а охват.** Отбор пачки работой +оставлял без присмотра ровно то, чего работа не касалась: правка, отменившая +решение, живёт в одном документе, а парный статус нужен в другом. Канон мал, +раз в спринт он читается целиком. + +Находки обоих — обычный материал переоценки: строка на замену идёт в документ +сразу, работа больше чем на абзац становится задачей типа `chore`. **Позвал — +скажи в докладе, кого именно позвал, и приложи границы покрытия**; не позвал — +скажи и это, иначе доклад читается как «сверено». ## Шаг 3. Переоценка задач @@ -138,14 +152,23 @@ на выход: новая возможность вне цели это возможность, которой никто не заказывал. Операционной задаче (`fix`, `chore`, `research`) цель не нужна, и выдумывать её здесь не надо. + + **Отменяется и сама цель** — когда замысел оказался неверен, а не когда + задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую + либо закрыть своей причиной, либо перевесить на другую цель, и только потом + закрыть цель. Порядок и почему он такой — + [task-goal.md](../../tasks/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель). + Здесь этому и место: отмена цели это разбор её задач, а разбор задач — этот + шаг. 8. **Задача ли это по-прежнему.** Не проходит тест «готова к взятию» → `edit --type research` и опустошённый раздел «Вопрос», то есть сырьё; дальше штурм. Разрослась → это несколько задач под той же целью, дальше декомпозиция. -9. **Переоценка по измеренному.** Спринт производит числа — сколько на самом - деле стоит такая работа, что оказалось дороже ожидания. Эти числа меняют цену - **других** задач, и именно здесь это применяется: задача, чья цена выросла - втрое, а польза осталась прежней, — кандидат на выход. +9. **Переоценка по пройденному.** Прошедший спринт показывает, чего на самом + деле стоит такая работа. Это меняет цену **других** задач, и именно здесь + применяется: задача, оказавшаяся заметно дороже, чем думалось, при прежней + пользе — кандидат на выход. Судит человек по тому, что помнит о прошедшем + спринте; замеров процесс не ведёт и оценок не хранит. ### Храповик на залежавшихся diff --git a/av-dev-pm/skills/session/references/sprint.md b/av-dev-pm/skills/session/references/sprint.md index 7c17a70..79db003 100644 --- a/av-dev-pm/skills/session/references/sprint.md +++ b/av-dev-pm/skills/session/references/sprint.md @@ -81,6 +81,16 @@ flowchart TD делается после ответа человека. Спринт не «ждёт»: ждать может человек, а замороженный набор, который нельзя двигать, только мешает. +**Протухший набор — второй законный повод роспуска.** Работа стояла, и человек +вернулся к спринту, состав которого уже не держит в голове. Тем же роспуском: +`sprint close --dissolve --reason "работа стояла с <когда>"`, недоделанное в +беклог, новый набор — после переоценки, а не поверх старого. + +Порога в неделях нет и не будет: счётчик простоя пришлось бы вести руками, а +решает всё равно человек. Признак — не срок, а **что набор перестал быть твоим**: +взялся перечитывать, зачем эти задачи вместе, — он протух. Заморозка тут не +мешает, она запрещает *двигать* набор, а не распустить его целиком. + ## Определение готовности Задача засчитывается сделанной, когда верно **всё**: diff --git a/av-dev-pm/skills/tasks/references/task-goal.md b/av-dev-pm/skills/tasks/references/task-goal.md index 3b246ed..2c5bf93 100644 --- a/av-dev-pm/skills/tasks/references/task-goal.md +++ b/av-dev-pm/skills/tasks/references/task-goal.md @@ -59,6 +59,27 @@ открытых задач. Файл удаляется, строка с датой переезжает в `Готово`. Скрипт откажет, если задачи ещё живы. +## Отменённая цель — сперва задачи, потом цель + +Замысел бывает неверен, и цель отменяют, не достигнув. Порядок обратный +завершению и держится тем же запретом: цель, закрытая поверх живых задач, +оставила бы их сиротами, и `close` этого не даст. + +1. **Разобрать её задачи поштучно.** Задача, теряющая смысл вместе с целью, — + `close --reason "<почему>"`; задача, переживающая цель, — `edit + --goal <другая>`. **Причина обязательна и пишется своя каждой:** «цель + отменена» это не причина, а пересказ команды, и в `REJECTED.md` от него нет + пользы через квартал. +2. **Закрыть саму цель** — `close <слаг> --reason "<почему замысел отменён>"`. + Файл удаляется, строка с причиной и датой уходит в `REJECTED.md`. В `Готово` + не попадает: `Готово` отвечает «что приложение умеет», а отменённая цель не + умеет ничего. + +**Место этому — переоценка на сессии, а не отдельный заход.** Отмена цели значит +разбор всех её задач, а разбор задач и есть шаг 3 сессии +([cadence.md](../../session/references/cadence.md), пункт 7). Отменять на ходу, +между делом, — верный способ закрыть скопом то, что стоило перевесить. + ## Что видит машина, а что человек `check` считает цели, различает разобранные и пустые, ставит `decomposed`, diff --git a/av-dev-pm/skills/tasks/scripts/tasks.py b/av-dev-pm/skills/tasks/scripts/tasks.py index 5c79f86..9076f7a 100755 --- a/av-dev-pm/skills/tasks/scripts/tasks.py +++ b/av-dev-pm/skills/tasks/scripts/tasks.py @@ -2033,7 +2033,9 @@ def cmd_close(lay: Layout, a: argparse.Namespace) -> int: if open_tasks: raise Usage(f"у цели {a.slug} осталось открытых задач: {len(open_tasks)}" f" ({', '.join(sorted(t[:-3] for t in open_tasks))})." - f" Цель закрыта, когда не осталось её задач") + f" Цель закрыта, когда не осталось её задач: каждую либо" + f" close --reason со своей причиной, либо edit --goal на" + f" другую цель") plan = Plan() today = datetime.date.today().isoformat() @@ -2373,7 +2375,8 @@ def cmd_sprint_close(lay: Layout, a: argparse.Namespace) -> int: f" --reason …). Роспуск при блокере — sprint close --dissolve --reason …") if a.dissolve: if not a.reason: - raise Usage("--dissolve без --reason: роспуск объясняется блокером") + raise Usage("--dissolve без --reason: роспуск объясняется —" + " сработавшим блокером или тем, что набор протух") drop = argparse.Namespace(slugs=sorted(n[:-3] for n in entries), reason=a.reason) if entries and (rc := cmd_sprint_drop(lay, drop)) != EXIT_OK: return rc