av-dev-pm расколот на av-dev-docs и av-dev-tasks
Плагин владел двумя разными вещами сразу — документацией проекта и учётом работ, — и это мешало обеим. Канон нельзя было поставить без задач, задачи без канона, а язык проектных текстов лежал внутри скилла canon и потому принадлежал половине. Теперь плагина два, каждый ставится сам по себе. av-dev-docs: скиллы canon, docs, init; агенты doc-consistency, doc-code-drift, doc-wording; скрипт docs.py. av-dev-tasks: скиллы tasks, session; агенты task-form, task-wording; скрипт tasks.py. Между собой они зовутся через пространство имён, а не по пути в чужое дерево. Все относительные ссылки, пересекшие границу плагина, сняты: tasks больше не указывает в canon, canon не указывает в tasks. Вместо ссылки — имя скилла и оговорка, что вызов может не разрешиться, и это исход, а не поломка. То, что нужно обоим дословно, стало вторым общим домом. Словарь «Сопровождение и эксплуатация» назван в трёх местах трёх плагинов — секция роадмапа, раздел «Эксплуатация» в architecture.md, тема ревью operations — и ни один из трёх им не владеет; он уехал в shared/operations.md, а canon.md и скилл задач везут копии. Три перечня «чем держат проект» уже разъезжались на «метриках и логах» против «мониторинга», так что ссылка тут не годится: плагин, поставленный в одиночку, получил бы указатель в никуда. Тем же способом язык: у av-dev-tasks появилась своя копия language.md. Копий стало 18 при 8 домах. Переименования разведены по смыслу, а не заменой строки: где речь о каноне — av-dev-docs, где об учёте задач — av-dev-tasks. В пайплайне таких мест одиннадцать, и оба адресата там встречаются вперемешку. Журналы (DECISIONS, TODO, HISTORY) намеренно не тронуты: они описывают состояние на момент записи. По той же причине оставлена наблюдённая строка в комментарии docs.py — она цитирует конфиг живого проекта, а не называет плагин. Не входит в этот заход и названо отдельно: слияние canon и docs в один скилл, разделение docs/.pm.json на два конфига и переезд openspec в пайплайн. Гейт зелёный: копии, фронтматтеры, диаграммы, json. Оба скрипта прогнаны после переезда — docs.py version и tasks.py check на фикстуре. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,304 @@
|
||||
---
|
||||
name: session
|
||||
description: "Ритуал между спринтами и ведение самого спринта: разбор накопившихся вопросов, разбор прошедшего спринта про процесс, переоценка задач порциями, выбор цели (или решение, что спринт без цели) и набор нового спринта с заморозкой. Плюс правила по ходу спринта — что врывается в замороженный набор, чем вопрос отличается от блокера, когда задача выходит из спринта, что считается сделанным и что идёт в доклад. Использовать, когда просят закрыть или начать спринт, собрать набор, разобрать вопросы, провести груминг/переоценку/ретроспективу, решить «что делать дальше» или доложить итоги, а также когда вернулись к проекту после перерыва и надо понять, где остановились. Формат и содержимое задач — скилл tasks."
|
||||
---
|
||||
|
||||
# Сессия между спринтами
|
||||
|
||||
Работа идёт спринтами: **набор задач, замороженный до конца спринта** — обычно
|
||||
под одну цель, но бывает и без неё. Между спринтами — одна сессия из четырёх шагов. Этот скилл владеет
|
||||
**ритуалом**: как сессия проводится и как спринт ведётся. Форматом и содержимым
|
||||
задач владеет скилл `tasks`, выполнением задачи — пайплайн проекта.
|
||||
|
||||
## Почему не Scrum
|
||||
|
||||
Терминология близка — спринт, груминг, определение готовности, ретроспектива, —
|
||||
и это удобно: не нужно изобретать слова. Но добрая половина Scrum существует
|
||||
ради синхронизации людей, которых здесь нет.
|
||||
|
||||
**Не берём:** тайм-бокс (спринт ограничен объёмом, а не временем), velocity и
|
||||
оценки в очках, ежедневный стендап (стендап — это и есть диалог), планирование
|
||||
отдельно от груминга (владелец беклога один), роль скрам-мастера.
|
||||
|
||||
**Берём:** цель спринта, заморозку набора, определение готовности, груминг —
|
||||
каждое потому, что снимает решение, которое иначе принимается заново каждый раз.
|
||||
**Ретроспективу берём содержанием, но не отдельным ритуалом:** она шаг той же
|
||||
сессии. Процесс личный, синхронизировать некого, а отдельная встреча ради трёх
|
||||
вопросов — та самая плата ритуалом без выгоды.
|
||||
|
||||
## Роли
|
||||
|
||||
**Человек** выбирает цель спринта — **или решает, что этот спринт без цели**, —
|
||||
разбирает вопросы, держит право на необратимое и на истину в самих данных.
|
||||
|
||||
**Агент — оркестрация.** Он собирает набор под названную цель, ставит задачи,
|
||||
принимает отчёты и докладывает. Кто именно делает задачу — исполнитель, сабагент,
|
||||
пайплайн — дело проекта; сессия про это не знает и знать не должна.
|
||||
|
||||
## Единицы
|
||||
|
||||
- **Цель** — то, ради чего набирается спринт. Файл типа `goal` (🎯),
|
||||
перечисленный в `ROADMAP.md`. Цель постоянна: живёт, пока живёт направление, —
|
||||
и уходит вместе с ним, если замысел оказался неверен (порядок отмены — в
|
||||
[tasks](../tasks/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель)).
|
||||
- **Задача** — то, что мерджится целиком и даёт видимую пользу.
|
||||
- **Вопрос** — решение человека. Не останавливает начатую работу, но **блокирует
|
||||
взятие** задачи в спринт. Живёт внутри файла задачи разделом «Вопросы» и тегом
|
||||
`question`.
|
||||
- **Блокер** — состояние, когда спринт не может продолжаться **ни одной**
|
||||
задачей.
|
||||
- **Спринт** — набор задач, замороженный до его конца. Под одной целью — или
|
||||
**без цели вовсе**, законно: багфикс, техдолг, спринт здоровья. Такой набор
|
||||
собран по работоспособности, а не по направлению, и заводится явно
|
||||
(`sprint start --no-goal`).
|
||||
|
||||
## Вопрос, блокер, необратимое
|
||||
|
||||
| | Что это | Когда спрашиваем | Что останавливает |
|
||||
| --- | --- | --- | --- |
|
||||
| **Вопрос** | решение человека | на сессии, пачкой | взятие задачи в спринт |
|
||||
| **Блокер** | спринт не может продолжаться ни одной задачей | немедленно | всё |
|
||||
|
||||
Право на **необратимое** — третье и отдельное: что именно необратимо, называет
|
||||
`CLAUDE.md` проекта, и спрашивается оно всегда, независимо от спринта.
|
||||
|
||||
**Блокер определяется исходом, а не одновременностью.** Встали разом или
|
||||
высыпались из спринта по одной — если продолжать нечем, это блокер: спринт
|
||||
распускается (`sprint close --dissolve --reason …`), человек спрашивается
|
||||
немедленно. Иначе спринт, из которого задачи вышли поштучно, выглядел бы штатно
|
||||
завершённым, а вопросы тихо ждали бы сессии.
|
||||
|
||||
**Отличать вопрос от застревания.** Правило про остаток принадлежит управлению
|
||||
задачами: оно решает, **сделана задача или вышла**, а это исход планирования, не
|
||||
исполнения. **Ниже канонический текст; пайплайн проекта на него ссылается, а не
|
||||
пересказывает** — два экземпляра одного правила разъезжаются, и разъезжаются
|
||||
незаметно, потому что расхождение видно только на редком входе.
|
||||
|
||||
> Есть остаток, который доводится без ответа, — задача продолжается, вопрос
|
||||
> записывается в файл. Остатка нет — задача выходит из спринта.
|
||||
|
||||
С двумя оговорками, без которых тест ошибается:
|
||||
|
||||
> **Остаток, который материализует нерешённое** — записывает в хранилище,
|
||||
> журнал, витрину **или наружу** состояние, зависящее от неотвеченного вопроса,
|
||||
> — **не остаток**. Решение поднимается до начала записи: откатить запись
|
||||
> дороже, чем подождать ответ, а иногда невозможно. «Наружу» — часть правила, а
|
||||
> не пример: выкладка, публикация и отправка данных третьей стороне не
|
||||
> откатываются тем более.
|
||||
|
||||
> **Пол для остатка:** остаток, из которого пропала польза, названная в «зачем», —
|
||||
> это не сделанная задача, а вышедшая из спринта.
|
||||
|
||||
## Заморозка набора
|
||||
|
||||
**Целей не больше одной.** Названа цель — набор служит ей: задача под чужой
|
||||
целью в спринт не попадает, даже если взять удобно (`sprint take` это и
|
||||
запрещает). **Задача с открытым вопросом в набор не берётся** — это верно всегда.
|
||||
|
||||
**Спринт без цели — законный случай, а не недосмотр.** Багфикс, техдолг,
|
||||
здоровье: работа на работоспособность, а не на направление. Цель не названа —
|
||||
сверять нечего, и в такой набор идёт что угодно готовое к взятию, в том числе
|
||||
задачи под разными целями. Заводится он **явно**, `sprint start --no-goal`:
|
||||
забытый флаг и решение человека иначе неотличимы, а это решение продуктовое.
|
||||
Взамен проверки цели остаётся доклад — спринт без цели **называется таковым и
|
||||
объясняется** одной строкой.
|
||||
|
||||
**Новая работа падает в беклог, а не в идущий спринт.** Решение «врываться или
|
||||
отложить» принимается один раз правилом, а не заново каждый раз. Врывается
|
||||
только два класса:
|
||||
|
||||
1. **Необратимый ущерб** — потеря, порча или утечка данных: то, что не чинится
|
||||
доделкой потом.
|
||||
2. **Сломан общий станок** — красная проверка, на которой стоит определение
|
||||
готовности **всех** задач набора. Это не новая работа, а починка того, на чём
|
||||
делается вся остальная.
|
||||
|
||||
Что в проекте считается необратимым ущербом и что — общим станком, называет
|
||||
`CLAUDE.md` проекта. Не названо — спрашиваем человека, а не решаем сами.
|
||||
|
||||
**Конец спринта** — когда каждая задача набора либо сделана, либо вышла с
|
||||
записанной причиной. Не «все сделаны»: иначе одна застрявшая задача держит
|
||||
спринт бесконечно. Пустой набор закрывается `sprint close` — скрипт не даст
|
||||
закрыть непустой.
|
||||
|
||||
Ведение спринта целиком — исходы задачи, определение готовности, приёмка,
|
||||
доклад — [references/sprint.md](references/sprint.md).
|
||||
|
||||
## Сессия: четыре шага в этом порядке
|
||||
|
||||
Это зависимость, а не список.
|
||||
|
||||
1. **Разбор вопросов.**
|
||||
2. **Разбор прошедшего спринта — про процесс, а не про задачи.** Здесь же оба
|
||||
судьи документов канона на весь канон разом, раз в спринт: `doc-consistency`
|
||||
(документы между собой) и `doc-code-drift` (документы против кода).
|
||||
3. **Переоценка задач** порциями.
|
||||
4. **Выбор цели и набор спринта.** Цель называет человек — либо называет, что
|
||||
этот спринт без цели; набор собирает агент и показывает **до старта работ**.
|
||||
|
||||
Рёбра подписаны тем, что ломается при их нарушении:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
check["tasks.py check (+ --fix)<br/>результат — строкой в доклад"]
|
||||
s1["1. Разбор вопросов<br/>пачкой, не больше трёх за раз"]
|
||||
s2["2. Разбор прошедшего спринта<br/>про процесс → docs/review.md"]
|
||||
s3["3. Переоценка задач порциями"]
|
||||
s4["4. Цель называет человек,<br/>набор собирает агент"]
|
||||
sprint["спринт: набор заморожен"]
|
||||
|
||||
check --> s1
|
||||
s1 --> s2
|
||||
s2 --> s3
|
||||
s1 -->|"неотвеченный вопрос → переоценка вслепую"| s3
|
||||
s3 -->|"без переоценки набор берётся из протухшего"| s4
|
||||
s4 --> sprint
|
||||
```
|
||||
|
||||
Схема — **сводка**: процедура каждого шага в
|
||||
[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).
|
||||
|
||||
## Инструмент
|
||||
|
||||
Тот же `tasks.py`, что у скилла `tasks` — оба скилла в одном плагине, путь
|
||||
общий: `tk="$CLAUDE_PLUGIN_ROOT/skills/tasks/scripts/tasks.py"`. Сессии нужны
|
||||
прежде всего:
|
||||
|
||||
```
|
||||
python3 $tk check --dir D # с этого начинается любая сессия
|
||||
python3 $tk list --dir D --questions # шаг 1: что накопилось
|
||||
python3 $tk list --dir D --tag sprint:<слаг> # шаг 3: урожай спринта, первая порция
|
||||
python3 $tk list --dir D --stale # шаг 3: дальше по залежалости
|
||||
python3 $tk list --dir D --goal <слаг> # шаг 4: кандидаты под названную цель
|
||||
python3 $tk sprint start --dir D --goal <слаг> # шаг 4: заводит и слаг спринта
|
||||
python3 $tk sprint start --dir D --no-goal # шаг 4: набор без цели, явным флагом
|
||||
python3 $tk sprint take --dir D <слаг> … # шаг 4: набор
|
||||
python3 $tk sprint close --dir D # конец спринта; --dissolve при блокере
|
||||
python3 $tk reopen <слаг> --dir D --reason … # приёмка не сошлась после закрытия
|
||||
```
|
||||
|
||||
`D` — каталог задач проекта, по канону всегда `docs/tasks`; `--dir` передаётся
|
||||
явно каждой командой. Вызов из чужого контекста описан в скилле `tasks`
|
||||
(«Переносимость»). **Коды выхода** — там же: 1 это дрейф в беклоге, 3 это
|
||||
«каталога нет», и ветвиться на них надо по-разному.
|
||||
|
||||
**Слаг спринта заводит `sprint start`** (по умолчанию — дата) и пишет его в
|
||||
`SPRINT.md`; всё заведённое **при открытом спринте** помечается `sprint:<слаг>`
|
||||
автоматически. Поэтому «первая порция — урожай прошедшего спринта» работает без
|
||||
чьей-либо памяти — но ровно до команды `sprint close`, которая `SPRINT.md`
|
||||
очищает. Отсюда порядок: **урожай заводится до закрытия, слаг для сессии берётся
|
||||
из отчёта `sprint close`** ([references/sprint.md](references/sprint.md)).
|
||||
|
||||
Правки задач делаются мутациями (`edit`, `move`, `close`), а не редактором:
|
||||
руками правится только тело файла. Это правило скилла `tasks`, здесь оно не
|
||||
пересказывается.
|
||||
|
||||
## Стимулы, которые процесс создаёт
|
||||
|
||||
Правило, которое можно обойти в свою пользу, будет обойдено.
|
||||
|
||||
**Приёмщик и исполнитель здесь совпадают, и это надо назвать вслух.** Задачу
|
||||
закрывает и двигает по индексам агент-оркестратор — тот же, кто её и сделал.
|
||||
Прежде границу держала механика: моста между плагинами не было, и закрыть задачу
|
||||
пайплайн физически не мог. Теперь мост есть, и защита у трёх обходов ниже —
|
||||
**только текстовая**. Опоры, которые остались настоящими:
|
||||
|
||||
- **независимый отчёт ревью** — артефакт, написанный не исполнителем; по нему
|
||||
сверяют состав прогона и урожай. Где он лежит, знает пайплайн проекта; при
|
||||
конвейере `av-dev-pipeline` это отчёт триажа в
|
||||
`openspec/changes/archive/<id>/review/` (до архивации — `changes/<id>/review/`);
|
||||
- **`SPRINT.md` под git** — `git log -p` показывает, что и когда было закрыто.
|
||||
Работает, только если закрытие **закоммичено**: удаление файла задачи и правка
|
||||
индекса, оставшиеся в рабочем дереве, никакой истории не образуют;
|
||||
- **`reopen <слаг> --reason`** — закрытие не окончательно. Приёмка человеком на
|
||||
сессии его отменяет, и это штатная операция, а не скандал.
|
||||
|
||||
Известные обходы:
|
||||
|
||||
- **Скрыть блокер** — он останавливает всё и выглядит как провал исполнителя.
|
||||
Защита: тест про остаток плюс прямая запись, что **объявление блокера
|
||||
неудачей не считается**.
|
||||
- **Не записать вопрос** на задаче-кандидате, чтобы не вычеркнуть её из
|
||||
ближайшего набора. Защита: вопросы кандидатов разбираются на той же сессии
|
||||
**вне очереди порции**.
|
||||
- **Занизить критерии приёмки**, раз они пол. Защита ослаблена: правит их тот же,
|
||||
кто по ним отчитывается. Остаётся требование, что расхождение критериев с
|
||||
сутью — **дефект критериев, о котором сообщают, а не молча дорабатывают**, и
|
||||
переоценка на сессии, где критерии видит человек.
|
||||
- **Сжать задачу до остатка** и отчитаться «сделана». Защита ослаблена там же.
|
||||
Пол для остатка — польза, названная в «зачем»; проверяет его человек при приёмке,
|
||||
и `reopen` — его инструмент.
|
||||
- **Объявить спринт без цели**, чтобы не задавать человеку продуктовый вопрос:
|
||||
набор без цели берёт что угодно, и собрать его можно молча. Защита: цели нет
|
||||
— это **ответ человека, а не умолчание** (`--no-goal` спрашивается так же, как
|
||||
цель), плюс строка доклада, называющая спринт бесцельным и объясняющая почему.
|
||||
Два бесцельных спринта подряд — предмет разбора процесса, а не мелочь.
|
||||
- **Занизить урожай** — не заводить найденное по ходу. Защита: поимённая сверка
|
||||
с **сохранённым независимым отчётом**, а не с прозой исполнителя. Каждая
|
||||
отложенная находка имеет либо слаг, либо строку «не заведена: причина».
|
||||
Нулевой урожай при непустом отчёте виден сразу.
|
||||
|
||||
**Проект без конвейера ревью — независимого отчёта нет, и это надо сказать, а не
|
||||
обойти молча.** Задачи делались руками или чужим пайплайном, сверять урожай не с
|
||||
чем: остаётся проза исполнителя, то есть тот же взгляд, что и у автора. Тогда
|
||||
защита от занижения урожая **снята**, и доклад спринта обязан нести строку «урожай
|
||||
сверялся с отчётом исполнителя — независимого отчёта в проекте нет». Дальше это
|
||||
решение человека: завести конвейер, принимать выборочной перепроверкой или
|
||||
согласиться с ценой. Молчание здесь хуже любого из трёх исходов.
|
||||
|
||||
Стимулы внутри пайплайна задачи (занизить требования к проверке, пропустить
|
||||
проход) принадлежат ему и защищены там же.
|
||||
|
||||
## Слоты проекта
|
||||
|
||||
Сессия не знает ни языка, ни сборки, ни CI. На часть проектного отвечает своей
|
||||
структурой канон документов (его ведёт плагин `av-dev-docs`): разбор процесса
|
||||
(шаг 2) живёт в `docs/review.md`, оракулы и «чем краснеет безусловно» — в
|
||||
семантике гейта в `CLAUDE.md`. Пути известны, ссылки в чужое дерево нет: канон
|
||||
ставится отдельно, а без него оба файла всё равно читаются по имени. Остальное проект **дописывает в `CLAUDE.md`**:
|
||||
|
||||
1. **Пайплайн задачи** — чем задача выполняется и что входит в его определение
|
||||
готовности. Сессия требует только форму: пайплайн пройден + критерии приёмки
|
||||
проверены поимённо.
|
||||
2. **Общий станок** — какая проверка, покраснев, врывается в замороженный
|
||||
спринт.
|
||||
3. **Необратимое** — что спрашивается у человека всегда (тот же слот, что у
|
||||
скилла `tasks`; дом один).
|
||||
4. **Ориентир по размеру спринта**, если он замерялся. Умолчание — 5–8 задач, и
|
||||
это **ориентир, а не закон**.
|
||||
|
||||
Слота «куда копируются критерии приёмки» здесь нет намеренно: на него отвечает
|
||||
**пайплайн проекта** — он переносит критерии в описание изменения, когда его
|
||||
заводит. Проект без пайплайна называет своё место сам, в слоте 1.
|
||||
|
||||
Числа проекта (сколько задач в спринте, сколько времени на задачу, каков прирост
|
||||
беклога) — предмет шага 2, а не константы этого скилла.
|
||||
|
||||
## Чего этот скилл не делает
|
||||
|
||||
Не пишет код и не выполняет задачи. Не заводит и не переоформляет задачи сам по
|
||||
себе — формат и содержимое ведёт `tasks` (сессия зовёт его операции). Не решает
|
||||
за человека, какая цель следующая. Не двигает набор идущего спринта.
|
||||
Reference in New Issue
Block a user