session стал groom: два вопроса вместо ритуала спринта
Предмет сузился до двух: что сейчас самое важное и что перестало быть важным. Ответ записывается порядком строк в беклоге. Из четырёх шагов сессии выжили два (вопросы, переоценка порциями), один заменился расстановкой очереди вместо набора спринта, два выпали. Приёмка — грумингу не по предмету, ритуала у неё больше нет, остаётся reopen; цена названа в тексте. Разбор процесса потерял якорь, и вместе с ним ушёл прямой вызов агентов doc-consistency и doc-code-drift — это починка, а не потеря: агенты принадлежат av-dev-docs, и груминг звал их мимо правила обращения к соседу. sprint.md удалён, cadence.md стал portions.md. Канон 12: запись в журнал велит проектам снести SPRINT.md и расставить порядок, с точным порядком шагов — сперва удалить файл, потом check --fix, иначе он увидит третий индекс и станет ругаться, а не чинить. Побочно: canon.md объявлял себя версией 7 при текущей 11. Пять версий дом канона врал о себе — машина сверяет константу скрипта, а прозу в заголовке не читает никто.
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
{
|
||||
"name": "av-dev-tasks",
|
||||
"source": "./av-dev-tasks",
|
||||
"description": "Задачи и цели каталогом markdown-файлов, у каждой записи тип, и тип задаёт её схему. Спринт под одну цель, ритуал между спринтами, проверка согласованности скриптом tasks.py. Задача выполняется чем угодно: пайплайна плагин не требует и сам его не зовёт."
|
||||
"description": "Задачи и цели каталогом markdown-файлов, у каждой записи тип, и тип задаёт её схему. Приоритет — порядок строк в беклоге, расставляет его скилл груминга. Проверка согласованности скриптом tasks.py. Задача выполняется чем угодно: пайплайна плагин не требует и сам его не зовёт."
|
||||
},
|
||||
{
|
||||
"name": "av-dev-code",
|
||||
|
||||
@@ -3496,3 +3496,60 @@ change нет — берём источником актуальные спек
|
||||
предписаниях.** Правило «не задним числом» защищает от подделки фактов, а не
|
||||
от починки инструкций: инструкция, ссылающаяся на несуществующее, — не
|
||||
свидетельство эпохи, а поломка с отложенным сроком.
|
||||
|
||||
## 57. Спринты отменены: приоритет стал порядком строк, `session` стал `groom` (2026-08-09)
|
||||
|
||||
**АЕАКП. Спринт отвечал на вопрос «что делать дальше» замороженным набором, а
|
||||
между наборами на этот вопрос не отвечал никто.** Процесс идёт задача за задачей,
|
||||
и набор перестал что-либо удерживать: он не синхронизировал (некого), не
|
||||
ограничивал по времени (тайм-бокс не брали) и не защищал от врывания (врывалось
|
||||
ровно два класса, оба назывались правилом). Осталась цена — обязанность собрать,
|
||||
показать, заморозить и распустить.
|
||||
|
||||
**Приоритет вернулся, и вернулся туда, где ему место.** Прежнее правило «порядка
|
||||
нет, есть цель» было обосновано **набором спринта**, и с ним потеряло опору.
|
||||
Приоритет — свойство очереди, а не задачи, поэтому его дом **индекс**: то же
|
||||
исключение из правила «файл — источник истины», что уже было у «в каком индексе
|
||||
лежит запись». Числом в файле он быть не мог — два соседних файла смогли бы
|
||||
утверждать одно место, а строка индекса противоречить обоим.
|
||||
|
||||
**Гейт готовности стоял на `sprint take` и чуть не исчез вместе с ним.** Это было
|
||||
единственное место, где запись судили целиком: тип, цель у `feature`, пустой
|
||||
раздел вопросов, схема типа. Без спринта момента не осталось бы вовсе, а узнают
|
||||
о недописанной задаче на приёмке, когда сверять уже не с чем. Момент назвали
|
||||
заново — команда `tasks.py ready <слаг>`, и зовёт её тот, кто берёт задачу.
|
||||
Отказ там **код 1, а не 2**: запись не дописана — это рабочая ситуация, а не
|
||||
ошибка употребления.
|
||||
|
||||
**`session` стал `groom`, и предмет сузился до двух вопросов** — что сейчас
|
||||
самое важное и что перестало быть важным. Из четырёх шагов прежней сессии выжили
|
||||
два (вопросы, переоценка порциями), один заменился (расстановка очереди вместо
|
||||
набора спринта), два выпали:
|
||||
|
||||
- **приёмка закрытых задач** — грумингу не по предмету. Ритуала у неё больше нет,
|
||||
остаётся `reopen` по требованию. Цена названа прямо: приёмка происходит только
|
||||
тогда, когда что-то уже бросилось в глаза;
|
||||
- **разбор процесса** — его якорем был прошедший спринт. Вместе с ним из скилла
|
||||
ушёл прямой вызов агентов `doc-consistency` и `doc-code-drift`, и это **не
|
||||
потеря, а починка**: агенты принадлежат `av-dev-docs`, и груминг звал их мимо
|
||||
правила обращения к соседу, без ветки «плагина нет». Груминг теперь только
|
||||
**называет повод** сверить канон, а когда их звать — решает их владелец.
|
||||
|
||||
**Побочно найдено:** `canon.md` — дом определения канона — объявлял себя версией
|
||||
7, когда скрипт шёл на 11. Пять версий дом врал о себе, и не заметил никто:
|
||||
машина сверяет версию проекта с константой скрипта, а прозу в заголовке не
|
||||
читает.
|
||||
|
||||
### Что из этого следует
|
||||
|
||||
190. **Правило, обоснованное механикой, умирает вместе с ней — и надо проверять,
|
||||
что вопрос умер тоже.** «Порядка нет» держалось на наборе спринта; набор
|
||||
ушёл, а вопрос «что делать дальше» остался и повис без ответа. Снимая
|
||||
механику, ищи не только то, что на ней стояло, но и то, на что она отвечала.
|
||||
191. **Гейт живёт в моменте, а не в команде.** Проверка готовности была свойством
|
||||
`sprint take` — и была бы потеряна как деталь удаляемой команды. Момент
|
||||
«запись впервые судят целиком» существует независимо от того, чем он
|
||||
назван, и переезжает вместе с процессом.
|
||||
192. **Версия в прозе, которую не читает машина, протухает молча.** Дом канона
|
||||
назвал себя версией 7 при текущей 11: сверка шла по константе скрипта, а
|
||||
заголовок документа не сверял никто.
|
||||
|
||||
@@ -26,7 +26,11 @@
|
||||
(`goal`, `feature`, `fix`, `chore`, `research`), и тип задаёт её схему;
|
||||
вычитывают их два отдельных прохода: `task-form` (форма записи) и
|
||||
`task-wording` (язык записей);
|
||||
- `session` — ритуал между спринтами и ведение спринта.
|
||||
- `groom` — груминг беклога: что сейчас самое важное и что перестало быть
|
||||
важным. Ответ записывается **порядком строк** — приоритет это свойство
|
||||
очереди, и живёт он в индексе. Интерактивный: разбирает вопросы пачкой,
|
||||
переоценивает порциями по 5–8, расставляет верх очереди с доводом на
|
||||
каждое движение.
|
||||
- **av-dev-code** — код по задачам: решение одной задачи и его проверка.
|
||||
Владеет `openspec/`. **Требует OpenSpec и сам его заводит.**
|
||||
- `openspec` — завести, настроить и **проверить** `openspec/` в проекте:
|
||||
@@ -71,7 +75,7 @@ flowchart TB
|
||||
end
|
||||
subgraph tasksp["av-dev-tasks — учёт работ"]
|
||||
direction LR
|
||||
session["session"] --> tasks["tasks"]
|
||||
groom["groom"] --> tasks["tasks"]
|
||||
end
|
||||
init --> tasks
|
||||
init --> osp
|
||||
|
||||
+1
-1
@@ -83,7 +83,7 @@ check` сверяет версию, но не то, что миграционн
|
||||
**Не выродились ли «границы покрытия» в шаблон.** Строка «что смотрели и чего не
|
||||
смотрели» обязательна в докладе `check`, `adopt`, интейка, штурма, сессии,
|
||||
спринта и всех четырёх агентов — семь и больше раз за сессию, и проверить её
|
||||
исполнение некому: приёмщик и исполнитель одно лицо (`session/SKILL.md`,
|
||||
исполнение некому: приёмщик и исполнитель одно лицо (`groom/SKILL.md`,
|
||||
«Стимулы»). Выродившаяся строка **хуже отсутствия**: доклад выглядит проверенным.
|
||||
|
||||
Приём не правится: это гипотеза об износе, а не находка, и менять работающее по
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
цикл SDD, конвейер ревью, OpenSpec), `av-dev-git`. Общее, что нужно нескольким
|
||||
дословно, живёт домом в `shared/` и уезжает копиями.
|
||||
|
||||
Канон документов — **версия 11**. Живые проекты стоят на 2–3 и на плагине
|
||||
Канон документов — **версия 12**. Живые проекты стоят на 2–3 и на плагине
|
||||
`av-dev-pm`, которого больше нет.
|
||||
|
||||
## 1. Живые проекты — вернуть в рабочее состояние
|
||||
@@ -39,11 +39,11 @@
|
||||
- [ ] удалить проектные копии: `.claude/skills/healthlog-{task,review}-pipeline`
|
||||
и девять `.claude/agents/healthlog-review-*.md`. Они прошлого поколения и
|
||||
после переезда указывают на документы, которых уже не будет
|
||||
- [ ] `av-dev-docs:canon` в режиме `adopt` — он приведёт проект к канону 11
|
||||
- [ ] `av-dev-docs:canon` в режиме `adopt` — он приведёт проект к канону 12
|
||||
сразу, картой и с подтверждением. Файл-в-файл здесь не расписан: раскладку
|
||||
знает скилл, и второй перечень разошёлся бы с ним
|
||||
- [ ] каталог задач — в `tasks/` корня (канон 11), не в `docs/tasks/`. Скилл
|
||||
задач зовётся из `adopt` сам
|
||||
- [ ] каталог задач — в `tasks/` корня (канон 11), не в `docs/tasks/`, и без
|
||||
`SPRINT.md` (канон 12). Скилл задач зовётся из `adopt` сам
|
||||
- [ ] гейт проекта: три шага вместо одного — `docs.py check`, `tasks.py check
|
||||
--dir tasks`, `openspec.py check`. **Второй и третий раньше не были
|
||||
нужны:** согласованность задач тянул за собой `docs.py`, форму `config.yaml`
|
||||
@@ -61,33 +61,16 @@
|
||||
`review-pipeline` — **ровно как в плагине**, и короткое имя
|
||||
может увести в устаревшую копию молча (REMAINING)
|
||||
|
||||
## 2. Учёт работ без спринтов
|
||||
## 2. Учёт работ без спринтов — что осталось
|
||||
|
||||
Решено: спринты отменяются, беклог и роадмап остаются. Причина — процесс идёт
|
||||
задача за задачей, и замороженный набор перестал что-либо удерживать.
|
||||
Сделано: спринт снят со скрипта и текстов, приоритет стал порядком строк в
|
||||
беклоге, гейт готовности переехал в `tasks.py ready`, `session` стал скиллом
|
||||
`groom`, запись 12 в журнал версий канона написана.
|
||||
|
||||
- [ ] снять спринт: `SPRINT.md`, команды `sprint *`, переходы схемы состояний,
|
||||
правило «задача живёт в одном индексе за раз» упрощается до беклога
|
||||
- [ ] **приоритет — явный порядок строк в беклоге.** Правило 4 скилла задач
|
||||
(«порядка нет, есть цель») переписывается целиком: оно обосновано тем, что
|
||||
«что делать дальше» отвечает набор спринта, — а набора больше нет.
|
||||
Записать, что приоритет это **свойство очереди, а не задачи**, и потому его
|
||||
дом индекс: то же исключение из правила 2, что уже есть у «в каком индексе
|
||||
лежит задача, знают индексы»
|
||||
- [ ] **перевесить гейт готовности.** Схема типа (обязательные разделы, ≥2
|
||||
критерия, границы) проверяется на `sprint take`. Спринта нет — момента нет;
|
||||
нужен `tasks.py ready <слаг>` или `check --task <слаг>`, иначе задача уедет
|
||||
в работу без критериев приёмки. Сейчас `resolve` держит это глазами: он
|
||||
отказывает сырью (`research` без «Вопроса») и называет строкой невыполненную
|
||||
схему у прочих типов — то есть **машина в этом месте не участвует**
|
||||
- [ ] `check --fix`: восстановленная строка индекса теряет позицию, а позиция
|
||||
теперь и есть приоритет. Класть в конец категории и печатать пометкой, что
|
||||
приоритет назначен не человеком
|
||||
- [ ] `session` → скилл груминга внутри `av-dev-tasks`: пересортировка беклога,
|
||||
разбор вопросов, переоценка. `references/sprint.md` в мусор, `cadence.md`
|
||||
переписать под ритуал без спринта
|
||||
- [ ] запись в журнал версий канона: проектам надо снести `SPRINT.md` и
|
||||
расставить порядок
|
||||
- [ ] прогнать груминг на живом беклоге — на фикстуре проверялись команды, а не
|
||||
сам разбор. Наблюдение к первому прогону: **не выродился ли шаг 4 в
|
||||
«оставить как есть»** — признак тот, что доклад не называет ни одного
|
||||
движения с доводом
|
||||
|
||||
## 3. Калибровка — блокирует переезд jellybit
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ flowchart TD
|
||||
в объявленных границах.
|
||||
|
||||
**Что остатком не является — правило живёт не здесь.** Канонический текст с обеими
|
||||
оговорками — в плагине `av-dev-tasks`, скилл `av-dev-tasks:session`, раздел
|
||||
оговорками — в плагине `av-dev-tasks`, скилл `av-dev-tasks:groom`, раздел
|
||||
`## Вопрос, блокер, необратимое`, подраздел «Отличать вопрос от застревания».
|
||||
Правило принадлежит управлению задачами, потому что решает **сделана задача или
|
||||
вышла**, — это исход планирования, а не исполнения. **Ссылайся, не
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Канон документов проекта
|
||||
|
||||
**Версия 7.**
|
||||
**Версия 12.**
|
||||
|
||||
Это **единственный дом определения канона**. Скиллы `init`, `canon` и `docs`
|
||||
читают его, а не пересказывают: три описания одной раскладки разъедутся, и
|
||||
|
||||
@@ -13,6 +13,49 @@ upgrade` идёт по записям снизу вверх от версии п
|
||||
|
||||
---
|
||||
|
||||
## Версия 12 — 2026-08-09
|
||||
|
||||
Спринты отменены. Работа идёт задача за задачей, и замороженный набор перестал
|
||||
что-либо удерживать: он отвечал на вопрос «что делать дальше», а между наборами
|
||||
на этот вопрос не отвечал никто.
|
||||
|
||||
**Что изменилось.** Индексов задач два вместо трёх: `SPRINT.md` упразднён.
|
||||
Приоритет стал тем, чем он и является, — **порядком строк в `BACKLOG.md`**:
|
||||
первая строка секции это то, что делают следующим. Назначает порядок человек,
|
||||
машина его не выводит; двигают его `move --after` и `move --first` с причиной.
|
||||
|
||||
Гейт готовности записи стоял на взятии задачи в спринт — единственном месте, где
|
||||
её судили целиком. Момент нужен и без спринта: теперь это команда
|
||||
`tasks.py ready <слаг>`, и зовёт её тот, кто берёт задачу в работу.
|
||||
|
||||
Ритуал между спринтами (`av-dev-tasks:session`) стал скиллом груминга
|
||||
(`av-dev-tasks:groom`): два вопроса — что сейчас самое важное и что перестало
|
||||
быть важным.
|
||||
|
||||
**Что сделать проекту.**
|
||||
|
||||
1. **Вернуть задачи из набора в беклог и снести `SPRINT.md`.** Порядок такой:
|
||||
`git rm tasks/SPRINT.md`, затем `tasks.py check --dir tasks --fix`. Строки
|
||||
набора после удаления файла становятся бездомными, и `--fix` возвращает их в
|
||||
беклог **в конец своей секции** — с пометкой, что позицию назначает человек.
|
||||
Наоборот делать нельзя: `check` без удалённого файла увидит третий индекс и
|
||||
станет ругаться на него, а не чинить.
|
||||
2. **Снять теги `sprint:<слаг>`** с записей — `tasks.py edit <слаг> --rm-tag
|
||||
sprint:<слаг>`. Тег больше никем не читается, а `check` о нём молчит: он
|
||||
законный свободный тег. Пропущенный вреда не сделает, но и пользы не несёт.
|
||||
3. **Расставить порядок** — первый груминг: `av-dev-tasks:groom`. После шага 1
|
||||
очередь состоит из того, что машина поставила в конец, то есть очереди нет
|
||||
вовсе. Пока порядок не назначен, «что делать дальше» по-прежнему без ответа.
|
||||
4. Поправить упоминания спринта в `CLAUDE.md` проекта, если они были: слот
|
||||
«общий станок» переехал в груминг под именем «что считается сломанным»,
|
||||
ориентир «5–8 задач в спринте» стал ориентиром размера порции разбора.
|
||||
5. `docs/.pm.json`: `"canon": 12`.
|
||||
|
||||
**Чего делать не надо.** `REJECTED.md`, `ROADMAP.md` и файлы `items/` не
|
||||
меняются: спринт жил только в собственном индексе и в тегах.
|
||||
|
||||
---
|
||||
|
||||
## Версия 11 — 2026-08-09
|
||||
|
||||
Каталог задач уехал из `docs/` в корень репозитория. Версия 8 отпустила его из
|
||||
|
||||
@@ -25,7 +25,7 @@ from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import NoReturn
|
||||
|
||||
CANON_VERSION = 11
|
||||
CANON_VERSION = 12
|
||||
|
||||
OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "av-dev-tasks",
|
||||
"description": "Задачи и цели каталогом markdown-файлов: одна запись — файл в items/ плюс строка ровно в одном индексе, у записи тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение из диалога с фильтром и дедупом, разбор находок ревью в задачи, декомпозиция на независимо полезные части, гигиена полей, согласованность индексов скриптом tasks.py. Спринт под одну цель с заморозкой набора и ритуал между спринтами. Записи вычитывают два прохода: task-form (форма записи) и task-wording (язык). Канон документов ведёт плагин av-dev-docs, он опционален. Задач не выполняет — этим занимается пайплайн проекта.",
|
||||
"description": "Задачи и цели каталогом markdown-файлов: одна запись — файл в items/ плюс строка ровно в одном индексе, у записи тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение из диалога с фильтром и дедупом, разбор находок ревью в задачи, декомпозиция на независимо полезные части, гигиена полей, согласованность индексов скриптом tasks.py. Приоритет — явный порядок строк в беклоге, и расставляет его скилл groom: интерактивный разбор на два вопроса, что сейчас самое важное и что перестало быть важным. Готовность записи к работе проверяет команда ready. Записи вычитывают два прохода: task-form (форма записи) и task-wording (язык). Канон документов ведёт плагин av-dev-docs, он опционален. Задач не выполняет — этим занимается пайплайн проекта.",
|
||||
"author": {
|
||||
"name": "Anton Vakhrushev",
|
||||
"email": "anwinged@gmail.com"
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
---
|
||||
name: groom
|
||||
description: "Груминг беклога — интерактивный разбор, отвечающий на два вопроса: что сейчас самое важное и что перестало быть важным. Ответ записывается порядком строк в беклоге: первая строка — то, что делают следующим. Разбирает накопившиеся вопросы, переоценивает задачи порциями по 5–8 (сделано попутно, отменено решением, слилось с соседней, подешевело, разрослось, стало сырьём), закрывает отжившее с причиной и расставляет очередь. Использовать, когда просят разобрать беклог, расставить приоритеты, решить «что делать дальше», провести груминг или переоценку, а также когда вернулись к проекту после перерыва и надо понять, где остановились. Формат и содержимое записей — скилл tasks; выполнение задачи — пайплайн проекта."
|
||||
---
|
||||
|
||||
# Груминг: что важно, что перестало
|
||||
|
||||
Скилл отвечает на **два вопроса**, и всё, что не служит им, — не его работа:
|
||||
|
||||
1. **Что сейчас самое важное?**
|
||||
2. **Что перестало быть важным?**
|
||||
|
||||
Ответ на оба **записывается порядком строк в беклоге**: первая строка секции —
|
||||
то, что делают следующим; то, что перестало быть важным, из беклога уходит с
|
||||
причиной. Приоритет — свойство очереди, а не задачи, и живёт он в индексе
|
||||
(правило 4 скилла `tasks`). Груминг — единственное место, где очередь
|
||||
назначается человеком.
|
||||
|
||||
**Скилл интерактивный.** Он не «приводит беклог в порядок» сам: суждение о
|
||||
важности принадлежит человеку, и весь ход — это подготовленные развилки с
|
||||
рекомендацией. Что решается фактом (сделано, отменено, дублируется), решается
|
||||
без вопросов и показывается списком.
|
||||
|
||||
Форматом и содержимым записей владеет скилл `tasks` — груминг зовёт его
|
||||
операции, а не правит файлы руками. Выполнением задачи — пайплайн проекта.
|
||||
|
||||
## Три правила, из которых всё следует
|
||||
|
||||
1. **Порядок назначает человек, машина его не выводит.** Ни давность, ни тип, ни
|
||||
число задач под целью приоритетом не являются. Единственное место в очереди,
|
||||
назначенное не человеком, — конец секции у сырья, и оно из очереди изъято
|
||||
(`tasks`, правило 4).
|
||||
2. **Порция важнее охвата.** Тридцать задач за заход — это усталость и
|
||||
штамповка: последние десять получат «оставить» не потому, что живы, а потому,
|
||||
что разбор затянулся. Лучше две честные порции, чем один полный проход.
|
||||
3. **Причина уезжает в запись.** Всё, что решено здесь, оставляет след:
|
||||
`--reason` у закрытия и переноса, ответ в теле задачи, строка в докладе.
|
||||
Решение, оставшееся в переписке, будет принято заново через месяц.
|
||||
|
||||
## Когда груминг созрел
|
||||
|
||||
**Зовёт человек.** Скилл сам себя не назначает, но обязан **напоминать**, и
|
||||
признак наблюдаемый, а не календарный:
|
||||
|
||||
- в беклоге появились записи, которых человек ещё не видел (заведены интейком по
|
||||
ходу работы, урожаем ревью, разбором находок);
|
||||
- на верхних строках очереди есть задача с открытым вопросом — очередь
|
||||
показывает то, что взять нельзя;
|
||||
- `tasks.py check` печатает «готово к взятию: 0 из N» — брать сегодня нечего.
|
||||
|
||||
Порога в неделях нет намеренно: счётчик простоя пришлось бы вести руками, а
|
||||
решает всё равно человек. Признак — **очередь перестала быть твоей**: взялся
|
||||
перечитывать, почему эти задачи стоят в таком порядке, — пора.
|
||||
|
||||
## Вопрос, блокер, необратимое
|
||||
|
||||
| | Что это | Когда спрашиваем | Что останавливает |
|
||||
| --- | --- | --- | --- |
|
||||
| **Вопрос** | решение человека | на груминге, пачкой | взятие задачи в работу |
|
||||
| **Блокер** | работа не может продолжаться ни одной задачей | немедленно | всё |
|
||||
|
||||
Право на **необратимое** — третье и отдельное: что именно необратимо, называет
|
||||
`CLAUDE.md` проекта, и спрашивается оно всегда, независимо от того, когда был
|
||||
последний груминг.
|
||||
|
||||
**Блокер определяется исходом, а не одновременностью.** Встали разом или
|
||||
задачи выпадали по одной — если продолжать нечем, это блокер, и человек
|
||||
спрашивается немедленно, а не ждёт ближайшего груминга.
|
||||
|
||||
**Отличать вопрос от застревания.** Правило про остаток принадлежит управлению
|
||||
задачами: оно решает, **сделана задача или вышла**, а это исход планирования, не
|
||||
исполнения. **Ниже канонический текст; пайплайн проекта на него ссылается, а не
|
||||
пересказывает** — два экземпляра одного правила разъезжаются, и разъезжаются
|
||||
незаметно, потому что расхождение видно только на редком входе.
|
||||
|
||||
> Есть остаток, который доводится без ответа, — задача продолжается, вопрос
|
||||
> записывается в файл. Остатка нет — задача возвращается в беклог.
|
||||
|
||||
С двумя оговорками, без которых тест ошибается:
|
||||
|
||||
> **Остаток, который материализует нерешённое** — записывает в хранилище,
|
||||
> журнал, витрину **или наружу** состояние, зависящее от неотвеченного вопроса,
|
||||
> — **не остаток**. Решение поднимается до начала записи: откатить запись
|
||||
> дороже, чем подождать ответ, а иногда невозможно. «Наружу» — часть правила, а
|
||||
> не пример: выкладка, публикация и отправка данных третьей стороне не
|
||||
> откатываются тем более.
|
||||
|
||||
> **Пол для остатка:** остаток, из которого пропала польза, названная в «зачем», —
|
||||
> это не сделанная задача, а вернувшаяся в беклог.
|
||||
|
||||
## Ход груминга
|
||||
|
||||
Четыре шага, и порядок — зависимость, а не список.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
check["tasks.py check (+ --fix)<br/>результат — строкой в доклад"]
|
||||
s1["1. Осмотреться<br/>что накопилось, чего человек ещё не видел"]
|
||||
s2["2. Разобрать вопросы<br/>пачкой, не больше трёх за раз"]
|
||||
s3["3. Что перестало быть важным<br/>порциями по 5–8"]
|
||||
s4["4. Что важно сейчас<br/>расставить порядок строк"]
|
||||
|
||||
check --> s1 --> s2 --> s3 --> s4
|
||||
s2 -->|"неотвеченный вопрос → судим о важности вслепую"| s4
|
||||
s3 -->|"без переоценки очередь строится из протухшего"| s4
|
||||
```
|
||||
|
||||
Схема — **сводка**: процедура каждого шага в
|
||||
[references/portions.md](references/portions.md), и при расхождении прав текст.
|
||||
|
||||
**1. Осмотреться.** `tasks.py check` (при дрейфе — `--fix`), затем показать
|
||||
человеку текущую очередь: верхние строки каждой секции и что появилось с
|
||||
прошлого раза. Это половина ответа на «что важно»: очередь, которую не видели,
|
||||
обсуждать бессмысленно.
|
||||
|
||||
**2. Разобрать вопросы.** Вопрос — решение человека, и разбирается он **пачкой**,
|
||||
а не по одному, как только возник: по одному это дёрганье, пачкой это груминг.
|
||||
Вопрос на верхних строках очереди разбирается **вне очереди порции**: иначе
|
||||
правило «задача с открытым вопросом в работу не берётся» создаёт стимул вопрос
|
||||
не записывать, лишь бы не вычеркнуть задачу из ближайшей работы.
|
||||
|
||||
**3. Что перестало быть важным.** Порциями по 5–8. Сперва то, что решается
|
||||
фактом и не требует ничьего суждения (сделано попутно, отменено решением,
|
||||
дублируется, симптомы одного дефекта), затем то, что решает человек (жива ли,
|
||||
та ли цель, задача ли это ещё).
|
||||
|
||||
**4. Что важно сейчас.** Расстановка порядка — `move --after <слаг>` и
|
||||
`move --first`. Разбирается **не весь беклог, а верх очереди**: первые три-пять
|
||||
строк каждой секции. Ниже пятой строки порядок всё равно перестаёт что-либо
|
||||
значить — до них дойдут после следующего груминга, и очередь к тому времени
|
||||
будет другой.
|
||||
|
||||
## Приоритет: как его расставляют
|
||||
|
||||
**Вопрос ставится сравнением, а не оценкой.** «Насколько важна эта задача» не
|
||||
имеет проверяемого ответа; «что из этих двух делают раньше» — имеет. Поэтому
|
||||
очередь строится попарно и сверху: что первое, что после него.
|
||||
|
||||
Доводы, которые принимаются:
|
||||
|
||||
- **что сломано сейчас** — работоспособность обгоняет развитие, и это не правило
|
||||
вкуса: сломанное дорожает само;
|
||||
- **что разблокирует остальное** — задача, после которой можно взять три другие,
|
||||
стоит раньше любой из трёх;
|
||||
- **что дешевеет от того, что сделано** — работа рядом с только что тронутым
|
||||
кодом стоит меньше, чем та же работа через квартал;
|
||||
- **что дорожает от ожидания** — данные копятся, миграция усложняется, внешний
|
||||
срок приближается;
|
||||
- **цель, которую человек назвал следующей.**
|
||||
|
||||
Довод **записывается причиной** (`move --after <слаг> --reason …`). Порядок без
|
||||
причины — это порядок, который на следующем груминге назначат заново с нуля.
|
||||
|
||||
**Цель и приоритет — независимые оси.** Очередь может идти поперёк целей, и это
|
||||
законно: задачи одной цели не обязаны стоять подряд. Но если под целью годами
|
||||
ничего не поднимается наверх — это разговор про цель, а не про очередь, и он
|
||||
идёт на шаге 3.
|
||||
|
||||
## Документы устаревают тем же ходом работы
|
||||
|
||||
Груминг судит **задачи**, а не документы, и агентов канона не зовёт: они
|
||||
принадлежат плагину `av-dev-docs`, и когда их звать — решает он.
|
||||
|
||||
Но повод назвать это здесь есть: беклог и документы протухают от одного и того
|
||||
же — от сделанной работы. Пришёл на груминг и видишь, что с прошлого раза сделан
|
||||
десяток задач, — скажи строкой, что канон стоит сверить (`av-dev-docs:canon`), и
|
||||
иди дальше. Плагина в проекте нет — сверять нечем, и это тоже строка.
|
||||
|
||||
## Интерактив
|
||||
|
||||
- Вопросы — через `AskUserQuestion`, **не больше трёх за раз**. Порция в 5–8
|
||||
задач обычно даёт больше трёх суждений: веди несколько итераций по ≤3, а не по
|
||||
одному вопросу на задачу и не одним перегруженным запросом.
|
||||
- К каждому варианту — **предварительное суждение, рекомендация первым
|
||||
вариантом**: «предлагаю выкинуть, потому что …». Возразить дешевле, чем судить
|
||||
с нуля.
|
||||
- Всё, что решается фактом, решай сам и показывай списком в докладе.
|
||||
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
|
||||
Между порциями — промежуточный доклад.
|
||||
|
||||
Примеры итераций, отбор порции, храповик на залежавшихся —
|
||||
[references/portions.md](references/portions.md).
|
||||
|
||||
## Стимулы, которые процесс создаёт
|
||||
|
||||
Правило, которое можно обойти в свою пользу, будет обойдено.
|
||||
|
||||
**Приёмщик и исполнитель совпадают, и это надо назвать вслух.** Задачу закрывает
|
||||
тот же агент, который её и сделал. Груминг приёмкой не занимается — отдельного
|
||||
ритуала у неё нет, — и настоящих опор остаётся две:
|
||||
|
||||
- **независимый отчёт ревью** — артефакт, написанный не исполнителем; при
|
||||
конвейере `av-dev-code` это отчёт триажа в
|
||||
`openspec/changes/archive/<id>/review/` (до архивации — `changes/<id>/review/`);
|
||||
- **`reopen <слаг> --reason`** — закрытие не окончательно. Заметил на груминге,
|
||||
что закрытая задача сделана не тем, чем обещала, — возвращай, это штатная
|
||||
операция, а не скандал. Индексы под git: `git log -p` по беклогу показывает,
|
||||
что и когда закрыто, потому что закрытие коммитится отдельным коммитом учёта.
|
||||
|
||||
Известные обходы:
|
||||
|
||||
- **Не записать вопрос** на задаче, которую хочется поднять наверх очереди.
|
||||
Защита: вопросы верхних строк разбираются вне очереди порции, шагом 2.
|
||||
- **Оставить всё как есть.** Груминг, на котором ничего не сдвинулось и ничего
|
||||
не закрылось, — это не «беклог в порядке», а не проведённый груминг. Защита:
|
||||
задача из верхних строк, которую и этот заход оставляет без изменений, **либо
|
||||
двигается, либо получает записанную причину**, почему её держат.
|
||||
- **Расставить порядок молча**, без доводов: тогда через месяц он неотличим от
|
||||
случайного. Защита: причина у каждого движения и строка доклада.
|
||||
- **Разобрать много и мелко** вместо немногого и важного: тридцать полей гигиены
|
||||
вместо трёх решений о важности. Защита: гигиена — работа скилла `tasks` и
|
||||
побочный продукт здесь; доклад называет **решения**, а не правки.
|
||||
|
||||
## Слоты проекта
|
||||
|
||||
Груминг не знает ни языка, ни сборки, ни CI. Проект дописывает в `CLAUDE.md`:
|
||||
|
||||
1. **Что считается сломанным** — какая красная проверка обгоняет развитие.
|
||||
Не названо — спрашиваем человека, а не решаем сами.
|
||||
2. **Необратимое** — что спрашивается всегда (тот же слот, что у скилла `tasks`;
|
||||
дом один).
|
||||
3. **Ориентир по размеру порции**, если он замерялся. Умолчание — 5–8 задач, и
|
||||
это **ориентир, а не закон**.
|
||||
|
||||
Числа проекта (сколько задач приходит за месяц, каков прирост беклога) — предмет
|
||||
наблюдения человека, а не константы этого скилла.
|
||||
|
||||
## Доклад
|
||||
|
||||
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
|
||||
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
|
||||
- **Что перестало быть важным**: удалено как реализованное (со ссылками), ушло
|
||||
без реализации (с причинами), понижено до сырья, слито, сменило тип или цель.
|
||||
- **Что важно сейчас**: верх очереди по каждой секции — слаги в порядке, и по
|
||||
каждому движению довод одной строкой.
|
||||
- **Границы покрытия**: сколько задач не трогали и какие именно секции, теги или
|
||||
цели остались — иначе доклад читается как «беклог разобран».
|
||||
- `tasks.py check` после правок — результат строкой.
|
||||
|
||||
## Чего этот скилл не делает
|
||||
|
||||
Не пишет код и не выполняет задачи. Не заводит и не переоформляет записи сам по
|
||||
себе — формат и содержимое ведёт `tasks` (груминг зовёт его операции). Не решает
|
||||
за человека, что важно: он готовит развилки и рекомендует. Не принимает
|
||||
закрытые задачи отдельным ритуалом — `reopen` есть, момента у него нет. Не судит
|
||||
документы проекта — это плагин `av-dev-docs`.
|
||||
@@ -0,0 +1,162 @@
|
||||
# Порции, разбор и расстановка
|
||||
|
||||
Процедура шагов 2–4 груминга. Рамка и правила — [SKILL.md](../SKILL.md).
|
||||
|
||||
Начинается всё с `tasks.py check` (и `check --fix`, если дрейф накопился) —
|
||||
результат идёт строкой в доклад.
|
||||
|
||||
## Шаг 2. Разбор вопросов
|
||||
|
||||
`tasks.py list --questions` — всё, что накопилось. Порядок по каждому вопросу:
|
||||
|
||||
1. **Проверь, не отвечен ли он уже** — решением, документом, соседним
|
||||
изменением, самим ходом сделанной с тех пор работы. Отвеченный вопрос не
|
||||
выносится человеку: это самая частая находка и она не требует ничьего
|
||||
решения.
|
||||
2. **Сформулируй развилку** с вариантами и последствием каждого, рекомендация —
|
||||
первым вариантом.
|
||||
3. **Вынеси пачкой** через `AskUserQuestion`, не больше трёх за раз.
|
||||
4. **Запиши ответ в тело задачи, опустоши раздел «Вопросы»**, сними тег
|
||||
(`edit <slug> --rm-tag question`), **перепиши «зачем»**: «Решено: …» на вопрос
|
||||
«почему это лежит в беклоге» уже не отвечает. Опустошение раздела — не
|
||||
уборка, а условие взятия: правило и причина в скилле `tasks`,
|
||||
[references/task-format.md](../../tasks/references/task-format.md).
|
||||
|
||||
**Вопросы на верхних строках очереди разбираются вне очереди порции** — здесь
|
||||
же, даже если сама задача в порцию переоценки не попала. Иначе правило «задача с
|
||||
открытым вопросом в работу не берётся» создаёт стимул вопрос не записывать, лишь
|
||||
бы не вычеркнуть задачу из ближайшей работы.
|
||||
|
||||
## Шаг 3. Что перестало быть важным
|
||||
|
||||
Цель — выкинуть то, что перестало быть задачей, и вернуть остальному честное
|
||||
состояние. Не «пересмотреть всё», а «пересмотреть порцию до конца».
|
||||
|
||||
### Порция и правило остановки
|
||||
|
||||
- **5–8 задач за порцию.** Размер обоснован усталостью, а не пропускной
|
||||
способностью, и менять его не надо — **надо брать несколько порций**.
|
||||
- **Отбор порций по порядку:**
|
||||
1. **свежее** — заведённое с прошлого груминга: оно ещё не проходило ни одной
|
||||
проверки на нужность. Свежесть меряется git'ом, как и залежалость, — датой
|
||||
появления файла в истории;
|
||||
2. дальше **по залежалости** — `list --stale`;
|
||||
3. по потребности — одна секция целиком, один тег (партия ревью), одна цель
|
||||
(`--goal`), список от человека.
|
||||
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
|
||||
Между порциями — промежуточный доклад.
|
||||
|
||||
### Что делать с каждой задачей
|
||||
|
||||
Сперва то, что не требует ничьего решения:
|
||||
|
||||
1. **Проверь, не сделано ли уже.** Задача, реализованная попутно в соседнем
|
||||
изменении, — самая частая находка. Смотри код, документацию, историю коммитов
|
||||
по ключевым словам. Удаление «как реализованной» деструктивно и без следа
|
||||
(в `REJECTED.md` реализованные не пишутся), поэтому порог улики жёсткий:
|
||||
`close <slug> --implemented` только имея **конкретный коммит или строку
|
||||
документа**, закрывающие задачу, и ссылка идёт в доклад. Есть лишь косвенные
|
||||
признаки — не удаляй сам, вынеси в пачку вопросов. Сделана частично → задача
|
||||
сжимается до остатка: тело правишь редактором, заголовок и «зачем» — через
|
||||
`edit`.
|
||||
2. **Проверь, не отменена ли решением.** Документ, ADR или архивное изменение
|
||||
мог закрыть вопрос иначе — тогда `close <slug> --reason "<ссылка на
|
||||
решение>"`. Задача закрывается не только коммитом.
|
||||
3. **Проверь пересечения.** Две задачи об одном — содержимое в одну, вторую
|
||||
`close <slug> --reason "слита с <другой-слаг>"`. Смотри **шире порции**:
|
||||
интейк дедуплицирует новое против существующего, но никогда не пересматривает
|
||||
уже лежащее, и две задачи с одной причиной могут лежать рядом месяцами.
|
||||
4. **Пере-кластеризуй по общей причине.** Несколько задач, оказавшихся симптомами
|
||||
одного дефекта, сливаются в одну — это находка, которую интейк дать не мог.
|
||||
5. **Гигиена полей** — протухшее «зачем», вопрос в прозе, снятый ответ, свойство
|
||||
репозитория в рамках, предписание процесса в теле, тип, разошедшийся с
|
||||
задачей, границы вместо реализации в разделе «Затрагивает». Список и правила —
|
||||
в скилле `tasks`. **Груминг — то самое место, где беклог добирает тип и
|
||||
разделы его схемы:** требовать их на входе значило бы выгонять в заметки то,
|
||||
что должно лежать задачей, а к взятию в работу они уже обязательны (`ready`).
|
||||
Блок здоровья `check` печатает, сколько записей готово к взятию, — по этому
|
||||
числу и видно, добрал ли груминг.
|
||||
|
||||
Гигиена — **побочный продукт, а не предмет**. Тридцать полей вместо трёх
|
||||
решений о важности означают, что груминг не состоялся.
|
||||
|
||||
Затем — то, что решает человек:
|
||||
|
||||
6. **Жива ли она вообще.** Контекст мог измениться: ушла зависимость, отпал
|
||||
сценарий, обошли иначе. Здесь и звучит вопрос, выкидывать ли.
|
||||
7. **Та ли цель — и нужна ли она вообще.** `feature`, которой не находится цель,
|
||||
— кандидат на выход: новая возможность вне цели это возможность, которой никто
|
||||
не заказывал. Операционной задаче (`fix`, `chore`, `research`) цель не нужна,
|
||||
и выдумывать её здесь не надо.
|
||||
|
||||
**Отменяется и сама цель** — когда замысел оказался неверен, а не когда
|
||||
задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую
|
||||
либо закрыть своей причиной, либо перевесить на другую цель, и только потом
|
||||
закрыть цель. Порядок и почему он такой —
|
||||
[task-goal.md](../../tasks/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель).
|
||||
8. **Задача ли это по-прежнему.** Не проходит `ready` по существу, а не по
|
||||
недописанным разделам → `edit <slug> --type research` и опустошённый раздел
|
||||
«Вопрос», то есть сырьё; дальше штурм. Разрослась → это несколько задач под
|
||||
той же целью, дальше декомпозиция.
|
||||
9. **Не подешевела ли она.** Сделанная с прошлого раза работа меняет цену
|
||||
**других** задач: рядом с только что тронутым кодом та же работа стоит меньше.
|
||||
Это довод и на шаге 4 — задача, внезапно подешевевшая, поднимается в очереди
|
||||
не потому, что стала важнее, а потому, что окно открыто.
|
||||
|
||||
### Храповик на залежавшихся
|
||||
|
||||
Сильно залежавшаяся задача — сигнал сама по себе: её либо ни разу не собирались
|
||||
делать, либо нечем взять. Измеряй наблюдаемым — датой последней правки из git
|
||||
(`list --stale` ставит такие первыми); счётчик «сколько грумингов пережила»
|
||||
нигде не хранится.
|
||||
|
||||
Задача из верхних строк `--stale`, которую и этот заход оставляет без изменений,
|
||||
**либо двигается (меняет цель, поднимается в очереди, уходит с причиной), либо
|
||||
остаётся с явно записанной причиной**, почему её держим (`move <slug> --section
|
||||
<та же> --reason …`). Молчаливое «оставить как есть» на давно неподвижной задаче
|
||||
— это решение не принимать решение; запись причины превращает его в осознанное и
|
||||
не даёт тому же вопросу всплыть на следующем груминге.
|
||||
|
||||
## Шаг 4. Что важно сейчас — расстановка
|
||||
|
||||
Разбирается **верх очереди**, а не весь беклог: первые три-пять строк каждой
|
||||
секции. Ниже пятой строки порядок всё равно перестаёт что-либо значить.
|
||||
|
||||
1. **Покажи текущий верх** — `list --index backlog`, по секциям, в том порядке,
|
||||
в каком строки лежат. Плюс состояние проекта из роадмапа: секция `Готово`
|
||||
отвечает на «где мы», `Запланировано` — на «куда шли».
|
||||
2. **Спрашивай сравнением, а не оценкой.** «Что из этих двух делают раньше»
|
||||
имеет проверяемый ответ, «насколько важна эта задача» — нет. Веди попарно и
|
||||
сверху: что первое, что после него.
|
||||
3. **Двигай командой, с причиной** — `move <slug> --after <другой> --reason …`
|
||||
или `move <slug> --first --reason …`. Довод берётся из перечня в
|
||||
[SKILL.md](../SKILL.md#приоритет-как-его-расставляют): сломано сейчас,
|
||||
разблокирует остальное, дешевеет от сделанного, дорожает от ожидания,
|
||||
названная цель.
|
||||
4. **Проверь верх на готовность** — `tasks.py ready <слаг> …` по первым строкам.
|
||||
Задача, стоящая первой и не проходящая `ready`, — это очередь, которая врёт:
|
||||
взять её нельзя. Либо дописывается здесь же, либо уступает место.
|
||||
|
||||
Пример одной итерации:
|
||||
|
||||
> **Верх секции «Игра», сейчас в таком порядке:**
|
||||
> `board-render-once` · `draw-before-full-board` · `move-parse-strict`
|
||||
>
|
||||
> 1. Что делаем первым?
|
||||
> - `draw-before-full-board` *(рекомендую)* — ничья объявляется на неполном
|
||||
> поле: игра врёт о результате, это сломано сейчас
|
||||
> - `board-render-once` — печать поля дублируется; мешает всякой правке
|
||||
> отрисовки, то есть разблокирует остальное
|
||||
> - оставить как есть
|
||||
> 2. `move-parse-strict` — третьей или выше?
|
||||
> - Оставить третьей *(рекомендую)* — ошибка ввода видна игроку сразу
|
||||
> - Поднять второй: тот же разбор трогает `board-render-once`, окно открыто
|
||||
|
||||
Каждый вариант несёт причину — ту самую, что уедет в `--reason`.
|
||||
|
||||
## Что делать, если разбирать нечего
|
||||
|
||||
Беклог пуст или в нём три задачи и все живые — груминг кончается за минуту, и
|
||||
это законный исход. Скажи строкой: очередь такая-то, сдвигать нечего. Придумывать
|
||||
работу, чтобы груминг «состоялся», — ровно тот ритуал без выгоды, от которого
|
||||
процесс избавлялся.
|
||||
@@ -1,304 +0,0 @@
|
||||
---
|
||||
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` — каталог задач проекта, всегда `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-code` это отчёт триажа в
|
||||
`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` (сессия зовёт его операции). Не решает
|
||||
за человека, какая цель следующая. Не двигает набор идущего спринта.
|
||||
@@ -1,277 +0,0 @@
|
||||
# Сессия: четыре шага
|
||||
|
||||
Одна сессия между спринтами. Порядок шагов — **зависимость, а не список**:
|
||||
переоценивать задачи, не разобрав вопросы, значит переоценивать вслепую; набирать
|
||||
спринт, не переоценив, значит набирать из протухшего.
|
||||
|
||||
Начинается сессия с `tasks.py check` (и `check --fix`, если дрейф накопился) —
|
||||
результат идёт строкой в доклад.
|
||||
|
||||
## Шаг 1. Разбор вопросов
|
||||
|
||||
`tasks.py list --questions` — всё, что накопилось. Вопрос это решение человека,
|
||||
и разбирается он **пачкой**, а не по одному, как только возник: по одному —
|
||||
это дёрганье, пачкой — это сессия.
|
||||
|
||||
Порядок по каждому вопросу:
|
||||
|
||||
1. **Проверь, не отвечен ли он уже** — решением, документом, соседним
|
||||
изменением, самим ходом прошедшего спринта. Отвеченный вопрос не выносится
|
||||
человеку: это самая частая находка и она не требует ничьего решения.
|
||||
2. **Сформулируй развилку** с вариантами и последствием каждого, рекомендация —
|
||||
первым вариантом.
|
||||
3. **Вынеси пачкой** через `AskUserQuestion`, не больше трёх за раз.
|
||||
4. **Запиши ответ в тело задачи, опустоши раздел «Вопросы»**, сними тег
|
||||
(`edit <slug> --rm-tag question`), **перепиши «зачем»**: «Решено:
|
||||
…» на вопрос «почему это лежит в беклоге» уже не отвечает. Опустошение
|
||||
раздела — не уборка, а условие взятия: правило и причина в скилле `tasks`,
|
||||
[references/task-format.md](../../tasks/references/task-format.md).
|
||||
|
||||
**Вопросы на задачах-кандидатах разбираются вне очереди порции** — здесь же, на
|
||||
этой сессии, даже если сама задача в порцию переоценки не попала. Иначе правило
|
||||
«задача с открытым вопросом в набор не берётся» создаёт стимул вопрос не
|
||||
записывать, лишь бы не вычеркнуть задачу из ближайшего спринта.
|
||||
|
||||
## Шаг 2. Разбор прошедшего спринта — про процесс, а не про задачи
|
||||
|
||||
Не «что мы сделали» (это доклад спринта, он уже был), а:
|
||||
|
||||
- **что сломалось в процессе и почему не поймали** — промах, доехавший до конца;
|
||||
- **что оказалось дороже, чем выглядело при заведении** — не число, а сам факт и
|
||||
причина: чего не было видно в постановке;
|
||||
- **какие правила не сработали или сработали не так** — в том числе правила
|
||||
этого плагина.
|
||||
|
||||
Замеров процесс не ведёт намеренно: оценки в очках и velocity не взяты
|
||||
(«[Почему не Scrum](../SKILL.md#почему-не-scrum)»), а спринт ограничен объёмом, а
|
||||
не временем — сравнивать «сколько заняло» не с чем. Разбор здесь качественный, и
|
||||
это не упущение.
|
||||
|
||||
**Артефакт обязателен.** Вывод, оставшийся в контексте сессии, не существует:
|
||||
следующая сессия его не увидит. Дом у него один и известен из канона —
|
||||
**`docs/review.md`**: вывод про конвейер и про то, что перестали проверять, идёт
|
||||
в раздел настройки, вывод про воспроизведённый дефект — в журнал. Решение с
|
||||
долгим следом — в `docs/adr/`.
|
||||
|
||||
Отдельным ритуалом ретроспектива не выделяется: процесс личный,
|
||||
синхронизировать некого.
|
||||
|
||||
**Здесь же зовутся оба судьи документов** — на весь канон разом, а не на пачку,
|
||||
отобранную работой:
|
||||
|
||||
- **`doc-consistency`** — согласованность документов между собой и с openspec:
|
||||
факт в двух домах, прямое противоречие, поведение в `architecture.md` вместо
|
||||
спек, ADR без парного статуса при замене, число без провенанса;
|
||||
- **`doc-code-drift`** — сверка с кодом по закрытому перечню фактов: имя основной
|
||||
ветки, команды, пути, внешние зависимости поимённо, настройки с числовым
|
||||
значением, единые точки проекта, capability.
|
||||
|
||||
Раз в спринт, а не чаще. Дорог из них по-настоящему первый — `doc-consistency`
|
||||
на `opus`: он сличает утверждения двух документов, и это суждение. Второй с
|
||||
недавних пор на `sonnet` — у него закрытый перечень фактов и команда на каждый, —
|
||||
но он читает репозиторий целиком, и дешёвым от смены модели не стал. Но и не
|
||||
реже — **спринт это ровно то, что двигает код и документы**:
|
||||
переименованная цель сборки, ушедшая зависимость, второй способ делать то, что
|
||||
обзор объявил единственным; факт, дописанный в `architecture.md`, уже живущий в
|
||||
`CLAUDE.md`. Протухшее и раздвоившееся неотличимо от свежего, и по нему принимают
|
||||
решения, пока кто-нибудь не наткнётся.
|
||||
|
||||
**Пачка — весь канон, и это не расточительство, а охват.** Когда пачку отбирала
|
||||
работа, без присмотра оставалось ровно то, чего работа не касалась: правка,
|
||||
отменившая решение, живёт в одном документе, а парный статус нужен в другом.
|
||||
Канон мал, раз в спринт он читается целиком.
|
||||
|
||||
Находки обоих — обычный материал переоценки: строка на замену идёт в документ
|
||||
сразу, работа больше чем на абзац становится задачей типа `chore`. **Позвал —
|
||||
скажи в докладе, кого именно позвал, и приложи границы покрытия**; не позвал —
|
||||
скажи и это, иначе доклад читается как «сверено».
|
||||
|
||||
## Шаг 3. Переоценка задач
|
||||
|
||||
Цель — выкинуть то, что перестало быть задачей, и вернуть остальному честное
|
||||
состояние. Не «пересмотреть всё», а «пересмотреть порцию до конца».
|
||||
|
||||
### Порция и правило остановки
|
||||
|
||||
Тридцать задач за один заход — это усталость и штамповка: последние десять
|
||||
получат «оставить» не потому, что живы, а потому, что сессия затянулась.
|
||||
|
||||
- **5–8 задач за порцию.** Размер обоснован усталостью, а не пропускной
|
||||
способностью, и менять его не надо — **надо брать несколько порций за
|
||||
сессию**.
|
||||
- **Сколько порций:** не меньше `⌈урожай прошедшего спринта / 8⌉`. Урожай — это
|
||||
задачи, заведённые за спринт; при урожае в 15 это две-три порции.
|
||||
- **Отбор порций по порядку:**
|
||||
1. **урожай спринта** — `list --tag sprint:<слаг>`: свежезаведённое ещё не
|
||||
проходило ни одной проверки на нужность. Тег на задачах проставлен
|
||||
автоматически при заведении — руками не метят и не вспоминают. **Слаг
|
||||
берётся из отчёта `sprint close`, а не из `SPRINT.md`:** сессия идёт после
|
||||
закрытия, а закрытие этот файл очищает;
|
||||
2. дальше **по залежалости** — `list --stale`;
|
||||
3. по потребности — одна секция целиком, один тег (партия ревью), одна цель
|
||||
(`--goal`), список от пользователя.
|
||||
- **Останавливайся на границе порции**, даже если «ещё чуть-чуть осталось».
|
||||
Между порциями — промежуточный доклад.
|
||||
|
||||
### Что делать с каждой задачей
|
||||
|
||||
Сперва то, что не требует ничьего решения:
|
||||
|
||||
1. **Проверь, не сделано ли уже.** Задача, реализованная попутно в соседнем
|
||||
изменении, — самая частая находка. Смотри код, документацию, историю коммитов
|
||||
по ключевым словам. Удаление «как реализованной» деструктивно и без следа
|
||||
(в `REJECTED.md` реализованные не пишутся), поэтому порог улики жёсткий:
|
||||
`close <slug> --implemented` только имея **конкретный коммит или строку
|
||||
документа**, закрывающие задачу, и ссылка идёт в доклад. Есть лишь косвенные
|
||||
признаки — не удаляй сам, вынеси в пачку вопросов. Сделана частично → задача
|
||||
сжимается до остатка: тело правишь редактором, заголовок и «зачем» — через
|
||||
`edit`.
|
||||
2. **Проверь, не отменена ли решением.** Документ, ADR или архивное изменение
|
||||
мог закрыть вопрос иначе — тогда `close <slug> --reason "<ссылка на
|
||||
решение>"`. Задача закрывается не только коммитом.
|
||||
3. **Проверь пересечения.** Две задачи об одном — содержимое в одну, вторую
|
||||
`close <slug> --reason "слита с <другой-слаг>"`. Смотри **шире порции**:
|
||||
интейк дедуплицирует новое против существующего, но никогда не
|
||||
пересматривает уже лежащее, и две задачи с одной причиной могут лежать рядом
|
||||
месяцами.
|
||||
4. **Пере-кластеризуй по общей причине.** Несколько задач, оказавшихся симптомами
|
||||
одного дефекта, сливаются в одну — это находка, которую интейк дать не мог.
|
||||
5. **Гигиена полей** — протухшее «зачем», вопрос в прозе, снятый ответ, свойство
|
||||
репозитория в рамках, предписание процесса в теле, тип, разошедшийся с
|
||||
задачей, границы вместо реализации в разделе «Затрагивает». Список и правила —
|
||||
в скилле `tasks`. **Переоценка — то самое место, где беклог добирает тип и
|
||||
разделы его схемы:** требовать их на входе значило бы выгонять в заметки то,
|
||||
что должно лежать задачей, а к взятию в спринт они уже обязательны. Блок
|
||||
здоровья `check` печатает, сколько записей готово к взятию, — по этому числу
|
||||
и видно, добрала переоценка или нет.
|
||||
|
||||
Затем — то, что решает пользователь:
|
||||
|
||||
6. **Жива ли она вообще.** Контекст мог измениться: ушла зависимость, отпал
|
||||
сценарий, обошли иначе. Здесь и звучит вопрос, выкидывать ли.
|
||||
7. **Та ли цель — и нужна ли она вообще.** Приоритетов нет, и «повысить» нечего
|
||||
— вместо повышения задача **меняет цель** (`edit <slug> --goal <другой>`) или
|
||||
входит в ближайший набор. `feature`, которой не находится цель, — кандидат
|
||||
на выход: новая возможность вне цели это возможность, которой никто не
|
||||
заказывал. Операционной задаче (`fix`, `chore`, `research`) цель не нужна, и
|
||||
выдумывать её здесь не надо.
|
||||
|
||||
**Отменяется и сама цель** — когда замысел оказался неверен, а не когда
|
||||
задача выбрала не ту. Тогда порция расширяется до всех задач этой цели: каждую
|
||||
либо закрыть своей причиной, либо перевесить на другую цель, и только потом
|
||||
закрыть цель. Порядок и почему он такой —
|
||||
[task-goal.md](../../tasks/references/task-goal.md#отменённая-цель--сперва-задачи-потом-цель).
|
||||
Здесь этому и место: отмена цели это разбор её задач, а разбор задач — этот
|
||||
шаг.
|
||||
8. **Задача ли это по-прежнему.** Не проходит тест «готова к взятию» → `edit
|
||||
<slug> --type research` и опустошённый раздел «Вопрос», то есть сырьё; дальше
|
||||
штурм. Разрослась → это несколько задач под той же целью, дальше
|
||||
декомпозиция.
|
||||
9. **Переоценка по пройденному.** Прошедший спринт показывает, чего на самом
|
||||
деле стоит такая работа. Это меняет цену **других** задач, и именно здесь
|
||||
применяется: задача, оказавшаяся заметно дороже, чем думалось, при прежней
|
||||
пользе — кандидат на выход. Судит человек по тому, что помнит о прошедшем
|
||||
спринте; замеров процесс не ведёт и оценок не хранит.
|
||||
|
||||
### Храповик на залежавшихся
|
||||
|
||||
Сильно залежавшаяся задача — сигнал сама по себе: её либо ни разу не собирались
|
||||
делать, либо нечем взять. Измеряй наблюдаемым — датой последней правки из git
|
||||
(`list --stale` ставит такие первыми); счётчик «сколько сессий пережила» нигде
|
||||
не хранится.
|
||||
|
||||
Задача из верхних строк `--stale`, которую и этот заход оставляет без изменений,
|
||||
**либо двигается (меняет цель, идёт в набор, уходит с причиной), либо остаётся с
|
||||
явно записанной причиной**, почему её держим (`move <slug> --section <та же>
|
||||
--reason …`). Молчаливое «оставить как есть» на давно неподвижной задаче — это
|
||||
решение не принимать решение; запись причины превращает его в осознанное и не
|
||||
даёт тому же вопросу всплыть на следующей сессии.
|
||||
|
||||
### Интерактив
|
||||
|
||||
- Вопросы — через `AskUserQuestion`, **не больше трёх за раз**. Порция в 5–8
|
||||
задач обычно даёт больше трёх суждений — тогда веди несколько итераций по ≤3,
|
||||
а не по одному на задачу и не одним перегруженным запросом.
|
||||
- К каждому варианту — **предварительное суждение, рекомендация первым
|
||||
вариантом**: «предлагаю выкинуть, потому что …». Пользователю дешевле
|
||||
возразить, чем судить с нуля.
|
||||
- Всё, что решается фактом (сделано / отменено / дублируется), решай сам и
|
||||
показывай списком в докладе, а не выноси в вопросы.
|
||||
|
||||
Пример одной итерации — три залежавшихся задачи, механику по ним уже разобрали:
|
||||
|
||||
> **Переоценка: 3 залежавшихся (порция по `--stale`)**
|
||||
>
|
||||
> 1. `versii-kachestvo-repaki` — версии и качество одного тайтла
|
||||
> - Выкинуть *(рекомендую)* — помечена «не боль», за полгода ни разу не возникла
|
||||
> - Оставить под целью `nadyozhnost-razdach`
|
||||
> - Перевести под цель `kachestvo-mediateki` — там она первая в очереди
|
||||
> 2. `backup-sqlite` — бэкап базы
|
||||
> - Оставить под текущей целью *(рекомендую)* — не сработала, но риск реальный
|
||||
> - Взять в ближайший набор — без бэкапа ретеншн опасен
|
||||
> - Выкинуть
|
||||
> 3. `guessit-sputnik` — вынести распознавание в сервис-спутник
|
||||
> - Понизить до сырья (`--type research`) *(рекомендую)* — не проходит тест «готова к взятию»
|
||||
> - Оставить задачей
|
||||
|
||||
Каждый вариант несёт причину — ту самую, что уедет в `--reason`. Ответы применяй
|
||||
сразу и, если в порции осталось ещё, следующей итерацией показывай следующие ≤3.
|
||||
|
||||
## Шаг 4. Выбор цели и набор спринта
|
||||
|
||||
1. **Покажи состояние проекта**: секцию `Готово` (что приложение уже умеет —
|
||||
это половина ответа на «где мы»), затем `Запланировано` с обоснованием
|
||||
очереди, `Направления`, и
|
||||
по каждой цели-кандидату — сколько под ней задач без открытых вопросов
|
||||
(`list --goal <слаг>`). Цель без готовых задач набором не станет: её сперва
|
||||
надо декомпозировать.
|
||||
2. **Цель называет человек** — либо называет, что цели не будет. Это
|
||||
продуктовое решение, а не механика: агент предлагает и объясняет, но не
|
||||
выбирает. **Оба ответа законны**, и «без цели» — такой же ответ, как слаг:
|
||||
спринт бывает под багфикс, под техдолг, под здоровье проекта. Спрашивается он
|
||||
так же, как цель, и в отдельный вопрос не выносится: это один и тот же вопрос
|
||||
«подо что набираем».
|
||||
3. **Набор собирает агент** — `sprint start --goal <слаг>` (или `sprint start
|
||||
--no-goal`), затем `sprint take …`. Скрипт не даст взять цель, задачу с чужой
|
||||
целью, с открытым вопросом, без типа и **без разделов, которых требует её
|
||||
тип** (у `fix` это в том числе `Воспроизведение`, у `research` — `Вопрос` и
|
||||
`Куда ляжет ответ`, и сырьё поэтому не берётся вовсе). Задача без цели (`fix`,
|
||||
`chore`, `research`) берётся свободно — операционная работа входит в набор
|
||||
помимо его цели. **В спринте без цели чужой цели нет вовсе**: сверять не с
|
||||
чем, берётся что угодно готовое, и единственной защитой остаётся показ набора
|
||||
человеку.
|
||||
4. **Набор показывается человеку до старта работ.** Показ — это и есть момент
|
||||
заморозки: после него набор не двигается. **В показе называется состав по
|
||||
типам** — три `fix` и ни одной `feature` под целью развития это разговор про
|
||||
цель, а не про набор, и увидеть его надо до заморозки, а не в докладе по
|
||||
итогам. **У набора без цели показ — единственная проверка состава**: скрипту
|
||||
там отказывать не по чему, и «что угодно готовое» превращается в осмысленный
|
||||
набор только глазами человека.
|
||||
|
||||
Здесь же последний дешёвый момент заметить **разнородную задачу**: раздел
|
||||
«Затрагивает» показывает границы до того, как заведено предложение об
|
||||
изменении. Строка, которая одна тянет задачу на метку выше остального
|
||||
перечня, — кандидат на разрез (шов — в `tasks`, `references/split.md`).
|
||||
Замеченная здесь, она стоит одного `edit`; замеченная на ревью — выброшенного
|
||||
предложения.
|
||||
5. Задача, которой для взятия не хватает только разделов её типа, дописывается
|
||||
здесь же — критерии с оракулами, перечень границ, шаги воспроизведения. Но
|
||||
если для этого нужен ответ человека, это вопрос, и задача в набор не идёт.
|
||||
|
||||
**Размер — ориентир, а не закон:** 5–8 задач. Можно взять больше, можно меньше —
|
||||
набор под цель важнее круглого числа; одна крупная задача спринтом тоже бывает.
|
||||
|
||||
## Доклад сессии
|
||||
|
||||
- Что просмотрено: N из M, сколько порций, по какому признаку отобраны.
|
||||
- Вопросы: разобрано N, из них отвечено без человека N, снято тегов N.
|
||||
- Разбор процесса: что записано и куда.
|
||||
- Сверка документов с кодом: звался ли `doc-code-drift`, что проверено из
|
||||
названного, что разошлось.
|
||||
- Изменения списком: удалено как реализованное (со ссылками), ушло без
|
||||
реализации (с причинами), понижено до сырья, слито, сменило тип или цель.
|
||||
- Новый спринт: цель — **или строка «без цели» с объяснением, почему** (багфикс,
|
||||
техдолг, здоровье), — набор со слагами, дата, состав по типам.
|
||||
- **Границы покрытия**: сколько задач не трогали и какие именно секции, теги или
|
||||
цели остались — иначе доклад читается как «беклог разобран».
|
||||
- `tasks.py check` после правок — результат строкой.
|
||||
@@ -1,199 +0,0 @@
|
||||
# Ведение спринта
|
||||
|
||||
Спринт — набор задач, замороженный до его конца: под одну цель или **без цели**
|
||||
(багфикс, техдолг, здоровье — это законно, `sprint start --no-goal`). Здесь то,
|
||||
что происходит **внутри** спринта: как задача заканчивается, что считается сделанным,
|
||||
кто принимает и что идёт в доклад. Как спринт набирается — шаг 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-tasks: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-code` — отчёт триажа в
|
||||
`openspec/changes/archive/<id>/review/`, до архивации — `changes/<id>/review/`),
|
||||
`SPRINT.md` под git и `reopen`. Переоценка на сессии и есть момент, когда
|
||||
критерии видит не исполнитель. **Конвейера ревью в проекте нет — первой опоры
|
||||
нет тоже**, и это называется строкой доклада, а не обходится молча
|
||||
(`SKILL.md`, «Стимулы»).
|
||||
3. **Расхождение — дефект критериев.** Приёмщик правит критерии и возвращает
|
||||
задачу исполнителю **в этом же спринте**: ответ есть, остаток есть, по тесту
|
||||
про остаток это не выход из спринта.
|
||||
|
||||
## Что врывается в замороженный набор
|
||||
|
||||
Только два класса — правило и его обоснование в SKILL.md. Здесь механика:
|
||||
|
||||
- вторжение **не добавляет** задачу в набор: `SPRINT.md` остаётся тем набором,
|
||||
который заморозили и показали. Внеплановая работа делается и называется в
|
||||
докладе отдельной строкой «внеплановое: что и почему»;
|
||||
- если внеплановое требует больше пары часов, честнее распустить спринт, чем
|
||||
делать вид, что набор соблюдается;
|
||||
- всё остальное падает в беклог через обычный интейк и ждёт сессии.
|
||||
|
||||
## Доклад в конце спринта
|
||||
|
||||
Проверяемые якоря, а не пересказ:
|
||||
|
||||
- **Цель спринта** — или строка «спринт без цели» с тем, чем он был (багфикс,
|
||||
техдолг, здоровье): у бесцельного набора это единственное место, где состав
|
||||
вообще объясняется. И по каждой задаче набора: **хеш коммита**, дословный
|
||||
исход проверок проекта, **исход по каждому критерию приёмки**.
|
||||
- **Какие развилки решались** и чем обоснованы.
|
||||
- **Урожай:** сколько задач заведено, какие вопросы накопились, что вышло из
|
||||
спринта и почему, что было внеплановым.
|
||||
- **Поимённая сверка урожая** с независимыми отчётами ревью: каждая отложенная
|
||||
находка имеет либо слаг, либо строку «не заведена: причина». Нулевой урожай при
|
||||
непустом отчёте — сигнал, а не благополучие. **Отчётов нет** (проект без
|
||||
конвейера ревью) — сверять не с чем, и строка доклада говорит именно это, а не
|
||||
«сверено».
|
||||
- **Созрела ли порция для сессии.** Решение звать — человека, напоминание —
|
||||
обязанность агента: `⌈урожай / 8⌉` порций.
|
||||
- **Границы покрытия** сжатой строкой: что в этом спринте не проверялось вовсе.
|
||||
Reference in New Issue
Block a user