Files
dev-skills/av-dev-pm/skills/session/references/sprint.md
T
avandClaude Opus 5 2d39a77444 ревизия покрытия av-dev-pm: три решения из шести оказались «убрать»
Сабагент в роли продакт-менеджера оценил покрытие жизненного цикла личного
проекта скиллами и агентами av-dev-pm. Скоуп сужен по ходу разбора: деплой и
разбор инцидентов делаются вручную, скиллов под них не заводим — три находки
из восьми сняты этим сразу.

Шаг 2 сессии требовал чисел, которых процесс отказался собирать решением.
cadence.md делал обязанностью пересмотр «ориентира по размеру спринта, прироста
беклога на закрытую задачу, времени на задачу» и «сколько заняли задачи против
ожидания». Данных нет: у записи нет дат заведения, взятия и закрытия, close
удаляет файл, sprint close очищает SPRINT.md. Хуже, «против ожидания» и «время
на задачу» требуют оценки и тайм-бокса, а session/SKILL.md в «Почему не Scrum»
их прямо не берёт — пункт противоречил решению через файл от себя. Числа не
пересматривались ни разу, поэтому выкинуты, а не подперты учётом дат. Осталось
качественное; рядом записано, что замеров нет намеренно, иначе следующий
читатель заведёт их обратно как недостающие. Шаг 3 пункт 9 переименован из
«переоценки по измеренному» в «по пройденному».

doc-consistency переехал с каждого синка на сессию, к doc-code-drift. Агент на
opus звался шагом 9 пайплайна, то есть 5-8 opus-проходов за спринт по документам,
меняющимся на несколько абзацев. Довод сильнее денег: расхождение между двумя
документами по определению требует двух, а на большинстве задач синк правит
один. И пачка, отбираемая работой, не видит того, чего работа не касалась, —
а расхождение живёт ровно там. Это снимает открытый вопрос REMAINING про охват
парного статуса ADR. Цена — потеря привязки находки к задаче, принято сознательно.

Отмена цели получила порядок, но не флаг. close запрещал закрыть цель с живыми
задачами и не говорил, что с ними делать. Теперь: сперва задачи поштучно
(close --reason своей причиной либо edit --goal на другую), потом цель в
REJECTED.md, а не в Готово. Флаг --cascade отвергнут: поштучный разбор — не
церемония, а единственный момент, когда видно, что переживёт цель. Место
процедуры — переоценка на сессии, отмена цели и есть разбор её задач.

У брошенного спринта появился второй законный исход. --dissolve везде был
привязан к блокеру, и вернувшийся к месячному набору не имел законного хода:
двигать нельзя, распускать не по чему. Теперь роспуск объясняется блокером или
тем, что набор протух. Порога в неделях нет — тот же класс, что выкинутые
числа: счётчик простоя пришлось бы вести руками. Признак не срок, а что набор
перестал быть твоим. Плюс точка входа «вернулся, а спринт открыт» и триггер в
description скилла.

Журнал канона прогоняется как есть, схлопывать 3 и 4 не стали. Взамен появилась
проверка исхода: шагом 6 adopt и шагом 6 upgrade зовутся оба судьи документов.
Это ответ на открытый вопрос «как проверять, что канон не разошёлся с проектами
после upgrade»: check сверяет число в .pm.json с версией скрипта и про существо
записи не знает ничего, а записи применяются руками.

Износ обязательных «границ покрытия» не правится: это гипотеза, а не находка.
Записана наблюдением к первой обкатке. Предложение агента поднять обкатку выше
калибровки снято — TODO уже так устроен, агент спутал «главный риск» с «первое
в очереди»; в REMAINING добавлена оговорка против того же прочтения.

Тема 31 в DECISIONS.md, следствия 117-123.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 14:02:04 +03:00

197 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Ведение спринта
Спринт — набор задач под одну цель, замороженный до его конца. Здесь то, что
происходит **внутри** спринта: как задача заканчивается, что считается сделанным,
кто принимает и что идёт в доклад. Как спринт набирается — шаг 4 в
[cadence.md](cadence.md).
## Наблюдаемые исходы задачи
Как они достигаются — дело пайплайна проекта. Сессия знает только исход и его
след.
- **Сделана** — по определению готовности ниже. `close <slug> --implemented`:
файл и строка удаляются, следом остаётся коммит. **Закрывает агент-оркестратор
последним шагом пайплайна, после коммита; приёмка человеком идёт позже и
отменяется `reopen`** — см. «Кто и когда закрывает».
- **Вышла из спринта** — `sprint drop <slug> --reason …`: возвращается в беклог
с вопросом в файле и **без живого незакоммиченного предложения** — иначе при
следующем взятии оно столкнётся с новым. Наработки, которые жалко терять,
переезжают в тело задачи текстом.
- **Оказалась крупнее задачи** — распознаётся **до того, как под неё заведено
предложение об изменении**, иначе его придётся выбрасывать. Выходит из набора,
уходит на декомпозицию; спринт продолжается остальными, части заводятся под той
же целью и в замороженный набор не добавляются.
- **Отменена решением по ходу** — `close <slug> --reason "<ссылка на решение>"`
прямо из спринта. Это редкий, но законный исход, и он называется в докладе.
**Конец спринта** — когда по каждой задаче набора наступил один из исходов. Не
«все сделаны»: иначе одна застрявшая задача держит спринт бесконечно. Затем
`sprint close`.
```mermaid
flowchart TD
take["sprint take — задача в наборе"]
done["сделана<br/>close --implemented"]
out["вышла<br/>sprint drop --reason"]
epic["крупнее задачи<br/>распознаётся до заведения change"]
cancel["отменена решением по ходу<br/>close --reason"]
all{"по каждой задаче набора<br/>наступил исход?"}
harvest["урожай заводится интейком tasks"]
close["sprint close"]
dissolve["sprint close --dissolve --reason<br/>недоделанное — в беклог"]
take --> done
take --> out
take --> epic
take --> cancel
done --> all
out --> all
epic --> all
cancel --> all
all -->|да| harvest
harvest -->|"тег sprint: ставится, пока SPRINT.md не очищен"| close
take -->|"продолжать нечем ни одной задачей — блокер"| dissolve
done -->|"приёмка не сошлась: reopen --reason"| take
```
Два ребра на схеме — те, где порядок обязателен и нарушается молча: **урожай до
`sprint close`** (после команды автотег уже не поставится) и **блокер в обход
исходов** (спринт распускается, а не ждёт).
Схема — **сводка**: определение готовности и правила приёмки ниже, и при
расхождении прав текст.
**Урожай заводится при закрытии спринта, а не при закрытии задачи.** Это
обязанность закрывающего: пройти по спискам находок от исполнителей и завести
недостающее интейком скилла `tasks` — с дедупликацией и картой человеку. Заводимое
метится тегом спринта само (`sprint:<слаг>`), поэтому первая порция следующей
сессии поднимается одной командой `list --tag sprint:<слаг>`. Спринт, закрытый
без этого шага, оставляет находки жить в отчётах — то есть нигде.
**Порядок здесь обязателен: урожай заводится ДО команды `sprint close`.**
Автотег ставится по слагу из `SPRINT.md`, а `sprint close` этот файл очищает;
заведённое после команды остаётся без тега и в первую порцию следующей сессии
не попадёт — молча, потому что пустой `list --tag` выглядит как «урожая не
было». Если так уже вышло, тег ставится руками: `add … --tag sprint:<слаг>`,
слаг берётся из отчёта `sprint close`.
**Провал спринта.** Сработал блокер — спринт распускается (`sprint close
--dissolve --reason …`), недоделанное возвращается в беклог, новый набор
делается после ответа человека. Спринт не «ждёт»: ждать может человек, а
замороженный набор, который нельзя двигать, только мешает.
**Протухший набор — второй законный повод роспуска.** Работа стояла, и человек
вернулся к спринту, состав которого уже не держит в голове. Тем же роспуском:
`sprint close --dissolve --reason "работа стояла с <когда>"`, недоделанное в
беклог, новый набор — после переоценки, а не поверх старого.
Порога в неделях нет и не будет: счётчик простоя пришлось бы вести руками, а
решает всё равно человек. Признак — не срок, а **что набор перестал быть твоим**:
взялся перечитывать, зачем эти задачи вместе, — он протух. Заморозка тут не
мешает, она запрещает *двигать* набор, а не распустить его целиком.
## Определение готовности
Задача засчитывается сделанной, когда верно **всё**:
1. **Пайплайн задачи пройден до конца** — со своим определением готовности, за
которое отвечает проект: проверки, состав ревью, документация, коммит. Здесь
оно не пересказывается и не подменяется — **форма фиксирована, содержание
даёт `CLAUDE.md` проекта**. Пайплайна нет, задача сделана руками — условие
читается как «проверки проекта зелёные и изменение влито».
2. **Критерии приёмки проверены поимённо** — каждый со своим оракулом, исход по
каждому назван. Это единственное, что добавляет управление задачами: пайплайн
отвечает «сделано по правилам», критерии — «сделано то, что заказывали».
3. **Находки по ходу отданы списком** — исполнитель обязан их **назвать**
(каждую, с пометкой «заведена / не заведена: причина»), но **не обязан
заводить**: заведение интерактивно, оно требует дедупликации против беклога и
кладбища и решений человека. Обязанность **завести урожай** — на закрытии
спринта, ниже. Так автономный исполнитель не оказывается одновременно обязан
завести задачи и не вправе это сделать в одиночку.
### Кто и когда закрывает
**Задачу закрывает агент-оркестратор — тот же, кто её и сделал**, последним шагом
пайплайна, после коммита. Порядок:
1. пайплайн доводит задачу до коммита;
2. **после коммита** зовёт `Skill av-dev-pm:tasks` и закрывает задачу
(`close <slug> --implemented`); строка уходит из `SPRINT.md`;
3. **докладывает исход и по каждому критерию — оракул и наблюдаемый исход.**
Это доклад приёмщику, а не отметка «принято».
**Приёмщик и исполнитель здесь совпадают, и это принято сознательно** — цена
названа в `SKILL.md`, раздел «Стимулы». Поэтому закрытие **не окончательно**, а
доклад по критериям — не формальность: он единственное, по чему приёмка вообще
возможна.
**Порядок «коммит, потом закрытие» обязателен.** Закрытие удаляет файл задачи;
упавший коммит после закрытия оставил бы задачу закрытой без единого следа
работы.
**Само закрытие тоже коммитится, отдельным коммитом.** Удаление файла задачи и
правка индекса — правки в рабочем дереве; пока они не в истории, `SPRINT.md`
ничего не показывает, а `reopen` восстанавливает текст из `HEAD` в узком окне,
которое закончится первым посторонним коммитом. Сообщение про учёт, а не про
работу: `закрыта задача <slug>`.
**Дорога назад существует и обязана быть названа.** Человек на сессии сверил
критерии, и приёмка не сошлась — `tasks.py reopen <slug> --reason "приёмка не
сошлась: …"`:
файл восстанавливается из истории git, строка возвращается в набор идущего
спринта (или в беклог, если спринта нет), строка кладбища снимается. Тело
восстанавливается **на момент удаления** — всё, что было дописано позже, живёт
только в коммите задачи, и это называется в докладе.
### Кто и по чему принимает
Три условия, без которых пункт про критерии не исполняется никем:
1. **Критерии переживают файл задачи.** Файл удаляется при закрытии, поэтому
критерии копируются туда, где их увидит приёмщик. Куда именно — **отвечает
пайплайн проекта, а не слот в `CLAUDE.md`**: он переносит их в `tasks.md`
изменения на шаге заведения change. Проект без пайплайна называет своё место
сам.
2. **Принимает человек на сессии, а не отдельный агент.** Декорреляция
исполнителя и приёмщика в момент закрытия **снята** (решение о снятии и его
цена — в `SKILL.md`, «Стимулы»). Опоры остались три: **сохранённый независимый
отчёт ревью** (при конвейере `av-dev-pipeline` — отчёт триажа в
`openspec/changes/archive/<id>/review/`, до архивации — `changes/<id>/review/`),
`SPRINT.md` под git и `reopen`. Переоценка на сессии и есть момент, когда
критерии видит не исполнитель. **Конвейера ревью в проекте нет — первой опоры
нет тоже**, и это называется строкой доклада, а не обходится молча
(`SKILL.md`, «Стимулы»).
3. **Расхождение — дефект критериев.** Приёмщик правит критерии и возвращает
задачу исполнителю **в этом же спринте**: ответ есть, остаток есть, по тесту
про остаток это не выход из спринта.
## Что врывается в замороженный набор
Только два класса — правило и его обоснование в SKILL.md. Здесь механика:
- вторжение **не добавляет** задачу в набор: `SPRINT.md` остаётся набором под
цель. Внеплановая работа делается и называется в докладе отдельной строкой
«внеплановое: что и почему»;
- если внеплановое требует больше пары часов, честнее распустить спринт, чем
делать вид, что набор соблюдается;
- всё остальное падает в беклог через обычный интейк и ждёт сессии.
## Доклад в конце спринта
Проверяемые якоря, а не пересказ:
- **Цель спринта** и по каждой задаче набора: **хеш коммита**, дословный исход
проверок проекта, **исход по каждому критерию приёмки**.
- **Какие развилки решались** и чем обоснованы.
- **Урожай:** сколько задач заведено, какие вопросы накопились, что вышло из
спринта и почему, что было внеплановым.
- **Поимённая сверка урожая** с независимыми отчётами ревью: каждая отложенная
находка имеет либо слаг, либо строку «не заведена: причина». Нулевой урожай при
непустом отчёте — сигнал, а не благополучие. **Отчётов нет** (проект без
конвейера ревью) — сверять не с чем, и строка доклада говорит именно это, а не
«сверено».
- **Созрела ли порция для сессии.** Решение звать — человека, напоминание —
обязанность агента: `⌈урожай / 8⌉` порций.
- **Границы покрытия** сжатой строкой: что в этом спринте не проверялось вовсе.