добавлены плагины av-dev-tasks и av-dev-pipeline

Пара плагинов с намеренно проведённой границей: av-dev-tasks отвечает
за то, что делаем и в каком порядке, av-dev-pipeline — за то, как ведём
одну задачу. Зависимости между ними нет: управление задачами работает и
с ручным исполнением, пайплайн — на проекте с любым учётом задач.

- av-dev-tasks — преемник av-dev-backlog: цели вместо приоритетов,
  спринт под одну цель с заморозкой набора, различение вопроса и
  блокера, каденция «вопросы — разбор — переоценка — набор».
  Раскладка docs/tasks с items/, PLAN.md, BACKLOG.md, SPRINT.md,
  REJECTED.md; проверенное из av-dev-backlog перенесено, не переписано.
- av-dev-pipeline — вынос того, что лежало копиями в healthlog и
  jellybit (3628 строк) и уже разошлось: цикл SDD, конвейер ревью с
  обязательным триажем, прогон нескольких задач разом. Проектная
  специфика вынесена в файл-бриф, charter'ы несут метод.

Коммит фиксирует состояние на момент ревью: три прохода нашли
блокирующие дефекты (нет шага, заводящего бриф; git rebase на занятой
worktree ветке; sprint drop пишет наполовину) — они чинятся следующими
коммитами. Сохранено как база, от которой видно правки.
This commit is contained in:
av
2026-08-03 11:01:29 +03:00
parent 092d07c15d
commit 9219f4a5cd
29 changed files with 5287 additions and 0 deletions
+236
View File
@@ -0,0 +1,236 @@
---
name: tasks
description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). Заведение задачи, идеи или цели из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, мозговой штурм идеи, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Ритуал между спринтами — скилл session. Не реализует задачи — этим занимается пайплайн проекта.
---
# Задачи
Задачи — каталог markdown-файлов. Одна запись = один файл `items/<slug>.md` плюс
строка **ровно в одном** индексе. Скилл владеет **форматом и содержимым**:
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует идеи.
Чем он **не** владеет: ритуалом между спринтами (разбор вопросов → разбор
прошедшего спринта → переоценка → выбор цели и набор) — это скилл `session`; и
выполнением задачи — это пайплайн проекта.
## Четыре правила, из которых всё следует
Ситуация не покрыта инструкцией — решай по ним.
1. **Беклог гниёт с той стороны, где его пополняют.** Заведение — самая частая
операция и с худшим отказом: из одного разговора рождается пять файлов, а
переоценка потом разгребает то, чего не надо было заводить. Дедупликация и
фильтр на входе дешевле любой чистки. Заводим только то, что **не делаем
сейчас** и о потере чего пожалеем.
2. **Файл — источник истины, индексы производны.** Разошлись — неправы индексы.
Согласованность механизируема и проверяется командой, а не вниманием: всё,
что ловит `tasks.py check`, не должно попадать ни в чек-лист, ни в промпт.
Единственное исключение намеренное: **в каком индексе лежит задача, знают
индексы** — «в спринте» это свойство спринта, а не файла, поля-состояния нет.
3. **Причина переживает запись.** Выкинутая без причины задача вернётся через
квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не
оставляет ничего, поэтому у неё есть `REJECTED.md`.
4. **Порядка нет, есть цель.** Ни в секциях, ни списком: «что делать дальше»
отвечает набор спринта, а между спринтами порядок не нужен никому — брать
задачи вне спринта запрещает заморозка. Поэтому нет ни приоритетов, ни
«повысить», ни «встать раньше»: вместо повышения — смена цели или включение
в набор.
## Раскладка
```
<tasks>/ по умолчанию docs/tasks, путь настраивается
items/ задачи и цели файлами, <slug>.md, слаги английские
PLAN.md оглавление целей: линия (упорядоченная) и кусты
BACKLOG.md что можно взять — только задачи, целей здесь нет
SPRINT.md текущий спринт: цель, набор, дата
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
```
Правило, снимающее путаницу: **`BACKLOG.md` — то, что берут; `PLAN.md` — то,
подо что берут.** Цель в спринт взять нельзя, поэтому в списке берущихся ей не
место.
**Задача живёт в одном индексе за раз.** Взята в спринт — строка переезжает из
`BACKLOG.md` в `SPRINT.md`; вышла — обратно. Файл в `items/` при этом **не
двигается**: он и есть запись, индексы лишь показывают, где она числится.
**У сделанной задачи записи не остаётся** — файл и строка удаляются (`close
--implemented`). Ей хватает коммита и документации проекта; вторая запись была
бы вторым домом для того же факта. Вопрос «что было в спринте N» отвечается
даром: `SPRINT.md` лежит под git, `git log -p <tasks>/SPRINT.md` отдаёт историю
всех наборов без отдельного журнала.
## Цели
**Цель — такой же файл в `items/`, тип `[goal]`**, перечисленный в `PLAN.md`:
либо звено упорядоченной **линии** продукта (с обоснованием порядка прозой),
либо тематический **куст** — цель, в последовательность не встающая («прочность
слияния», «журнал и пересборка»). Без второй части половина целей была бы нигде
не перечислена: находки ревью не служат ничему из линии.
- **Список задач цели выводится, а не хранится.** В теле цели — зачем она и что
считается её завершением; перечня задач там нет. Он был бы третьим индексом и
поехал бы на первой же закрытой задаче, а `check` про него не знает. Связь
однонаправленна: задача несёт тег `goal:<слаг>`, перечень даёт
`tasks.py list --goal <слаг>`.
- **Статус цели выводится.** Цель закрыта, когда у неё не осталось открытых
задач; `[x]`/`[~]` руками не ведутся, а `close` цели с живыми задачами
скрипт запретит. Единственная оговорка: цель без задач неотличима — «ещё не
декомпозирована» или «всё закрыто». Различает пометка «декомпозирована» в
теле, проставляемая при переоценке.
- **`[goal]` и `[epic]` — разные вещи.** Цель **постоянна**: живёт, пока живёт
направление. Эпик **временен**: это задача, которая не мерджится целиком, её
разбирают, и он исчезает. Два срока жизни под одним словом разъезжаются,
поэтому слова два.
## Инструмент (`tasks.py`)
Пусть `tk="$CLAUDE_PLUGIN_ROOT/skills/tasks/scripts/tasks.py"`.
```
python3 $tk check # согласованность всех индексов + здоровье, exit 1 при расхождениях
python3 $tk check --fix # + починить безопасный дрейф (секция, заголовок, дубли)
python3 $tk list [--stale] [--section S] [--type T] [--tag T] [--goal S] [--index …] [--questions]
python3 $tk add --slug S --title T [--type goal|idea|epic] [--section S] [--goal G] [--hook H] [--tag a,b]
python3 $tk edit S [--title T] [--hook H] [--type T] [--goal G] [--add-tag a,b] [--rm-tag c]
python3 $tk move S --section S [--reason R] [--after S | --first]
python3 $tk close S --reason R # в REJECTED.md + удалить (ушла без реализации)
python3 $tk close S --implemented # просто удалить (реализована, есть коммит)
python3 $tk sprint start --goal S | take S… | drop S… --reason R | close [--dissolve --reason R]
python3 $tk init [--dir D] [--sections …] [--plan-sections …] [--items …] [--backlog …] …
```
Тип — английское ключевое слово `goal` / `idea` / `epic` / `task` (как и прочие
токены команд); `task` префикса не несёт, остальные кодируются `[goal]`/
`[idea]`/`[epic]` в заголовке. Текст задачи при этом русский.
**Мутации правят файл и индексы заодно** — руками строку индекса или мета-строку
не пиши, зови `add`/`edit`/`move`/`close`/`sprint`. Смена заголовка, хука, типа,
цели и **тегов** — это `edit`: он держит H1, мета-строку и индекс в синхроне.
Снятие тега — `--rm-tag` (после ответа на вопрос снимается `question`), смена
цели — `--goal`, он заменяет прежний `goal:*`. Тело задачи скрипт не трогает:
`add` кладёт заголовок, мета-строку и шаблон с подсказками, тело дописываешь
редактором (пока плейсхолдер на месте, `check` напоминает).
`check` — единственный судья согласованности; что именно он ловит, скажет его
вывод, здесь не пересказываем. Гоняй его **в начале сессии** и **после каждой
правки**, даже если правил мутациями: дрейф мог накопиться раньше. Накопившееся
чини `check --fix` — он детерминированно правит безопасное, а неоднозначное
(ссылка на исчезнувший файл, задача сразу в двух индексах) выносит тебе. Это
идёт строкой доклада.
Формат файла, мета-строки, слага, индексов и `REJECTED.md`
[references/task-format.md](references/task-format.md). Там же тест «готова к
взятию» и требования к критериям приёмки.
## Сценарии
### Завести задачу, идею или цель из диалога
1. **Фильтр.** Делаем прямо сейчас — не заводим. Не пожалеем о потере — не
заводим. Родилось три кандидата — покажи их и спроси, какие заводить: молча
заведённая пачка и есть тот самый отказ из правила 1.
2. **Дедуп.** `list` плюс поиск по слагам, хукам и телам (`grep -ril`),
**включая `REJECTED.md`**. Нашлось среди живых — **дописываем в существующий
файл**, а не заводим соседний. Нашлось в `REJECTED.md` — покажи пользователю
ту строку и что изменилось с момента отказа (`add` предупредит и сам, но
молча заводить нельзя). Две задачи об одном — самая дорогая находка
переоценки.
3. **Тип по тесту готовности** (см. task-format): проходит — задача, не
проходит — идея (`--type idea`), проходит по пользе, но не делается одним
заходом — эпик (`--type epic`, сперва декомпозиция). Направление, а не
работа — цель (`--type goal`).
4. **Цель задачи.** У каждой задачи должен быть `--goal <слаг>`: задача вне цели
не попадёт ни в один спринт. Подходящей цели нет — либо она заводится
(`--type goal` кустом), либо это сигнал, что задача никому не служит и
заводить её не надо. У идеи цели может не быть — она проставляется, когда
идея становится задачей.
5. `add …`, затем допиши тело редактором: одна фраза, критерии приёмки с
оракулами, рамки. Хук отвечает «почему это лежит в беклоге» — состояние,
остаток, боль, — а не пересказывает первый абзац, и пишется **для человека**:
не «канонизация внутри транзакции», а «тело 40 МиБ держит блокировку 5 секунд,
соседние доставки уходят в отказ».
6. `check`.
### Разобрать находки аудита или ревью
Ревью и аудиты — тоже источник задач, но с зеркальной диалогу опасностью: не
пять файлов из одной мысли, а сорок файлов из сорока сырых находок. Защита та
же, что в самом ревью: кластеризация по причине, дедуп против живых и
`REJECTED.md`, находка без свидетельства → идея, а не задача, и карта кластеров
пользователю до создания файлов. Порядок, отображение серьёзности и привязка к
целям — [references/from-review.md](references/from-review.md).
### Декомпозиция и штурм идеи
[references/split.md](references/split.md). Обе операции превращают одну запись в
несколько, и у обеих есть проверяемый тест: части должны **мерджиться порознь** и
**каждая давать видимую пользу**, а у штурма исход «выкинуть» — полноправный.
### Гигиена полей
Правится по ходу любой операции, которая задачи касается (но не «заодно» по
всему беклогу):
- **протухший хук** — задача изменилась, а хук отвечает на старый вопрос;
особенно после ответа на вопрос задачи: «Решено: …» на «почему это лежит в
беклоге» уже не отвечает, хук переписывается;
- **вопрос, застрявший в прозе** — вынимается в раздел «Вопросы» плюс тег
`question` (`edit --add-tag question`), иначе он не виден ни `list
--questions`, ни правилу «задача с открытым вопросом в набор не берётся»;
- **тег, который некому снять** — `question` после ответа снимается `edit
--rm-tag question` вместе с записью ответа в тело;
- **свойство репозитория в рамках** — номер миграции, хеш, версия зависимости:
в лежалой задаче протухает молча и становится ложной рамкой. Снимается;
снимок берётся при постановке, а не при заведении;
- **предписание процесса в теле** — «делать таким-то профилем ревью», «взять
такой-то агент»: это второй дом для правила выбора и путь понизить требования
решением, принятым до проектирования. Снимается.
## Слоты проекта
Скилл не знает ни языка программирования, ни сборки, ни CI, ни трекера — задачи
для него просто каталог markdown. Всё проектное живёт в `CLAUDE.md` проекта, и
**проект обязан дописать туда**:
1. **Путь каталога задач**, если он не `docs/tasks`, и **секции беклога** — по
умолчанию `ядро` / `инфра`; граница между ними режется по существу работы, а
не по её поводу. Имена индексов и подкаталога, если они другие, задаются
`init` и живут в `<tasks>/.tasks.json`.
2. **Что такое «сделана»** — чем задача выполняется (пайплайн проекта) и что
входит в его определение готовности. Скилл требует лишь **форму**: пайплайн
проекта пройден + критерии приёмки проверены поимённо.
3. **Куда переезжает суть реализованной задачи** — спеки, ADR, архив изменений:
без этого не проверить, что задача закрыта не коммитом, а решением.
4. **Что считается необратимым** и потому спрашивается у человека всегда
(деплой, выкладка наружу, удаление или перезапись данных).
5. **Оракулы, которые в проекте вообще есть** — чем проверяется критерий
приёмки: тест, команда, прогон на реальных данных, глазами по логу.
Ничего из этого скилл не угадывает: не нашёл — спрашивает пользователя, а не
подставляет умолчание.
## Общее для всех сценариев
- **Развилки — пользователю.** Через `AskUserQuestion`, с уже сформулированным
предварительным суждением (**рекомендация — первым вариантом**). Что выкинуть,
под какую цель отнести, какая рамка идеи верна — решение пользователя. Слаг,
формулировка, порядок строк в индексе — механика, делаем сами.
- **Не больше трёх вопросов за раз.** Пачка длиннее трёх тяжела для ответа;
решений больше — веди **несколько итераций** диалога по ≤3, а не один
перегруженный запрос. Между итерациями применяй уже решённое.
- **Границы покрытия в отчёте.** Любая сессия разбора, штурма или интейка
заканчивается строкой «просмотрено N из M, не трогали — …». Отчёт без неё
сообщает «беклог разобран», не сообщая, какая его часть осталась нетронутой.
- **Ничего не удаляем молча.** Файл исчезает только через `close` — `--reason`
(ушла без реализации) или `--implemented` (реализована). Прямого `rm` нет.
- **Слаги английские**, kebab-case, не транслит: `tie-break-equal-completeness`,
а не `taj-brejk-pri-ravnoj-polnote`. Заголовки, тела и хуки — русские.
## Чего этот скилл не делает
Не пишет код, не заводит спеки и предложения об изменении, не берёт задачу в
работу — этим занимается пайплайн проекта. Не ведёт спринт и не проводит сессию
между спринтами — это `session`. Не решает за пользователя, что важно. Не
переоформляет существующие задачи «заодно»: правится то, чего касается операция.
@@ -0,0 +1,102 @@
# Задачи из аудита и ревью
Ревью и аудиты — код-ревью, архитектурный проход, аудит безопасности, любой
разбор другим агентом — порождают находки, часть которых становится задачами.
Это отдельный интейк со своей опасностью, **зеркальной** интейку из диалога.
- Интейк из диалога грешит переполнением: из одной мысли рождается пять файлов.
- Интейк из ревью грешит сваливанием: сорок сырых находок превращаются в сорок
файлов. Беклог раздувается, а следующая переоценка склеивает их обратно.
Защита от сваливания — та же, что в самом ревью: **кластеризация по причине, а
не файл-на-находку.** Если у ревью был триаж — половина работы уже сделана, бери
его выход. Если нет — триажируй сам, прежде чем заводить.
## Находка агента — не задача
Мнение агента — **гипотеза, пока у неё нет свидетельства** (падающий тест,
воспроизводимый шаг, положение гайда). Согласие нескольких находок само по себе
достоверность не повышает: это один источник, высказавшийся несколько раз.
Отсюда фильтр входа, поверх обычного «не делаем сейчас + пожалеем о потере»:
- **Находка со свидетельством**, отложенная к исполнению → **задача**.
Свидетельство и последствие переносим в тело — это её «почему», то самое, что
переживает запись.
- **Находка без свидетельства / низкой уверенности** → **идея** (`[idea]`), а не
задача. Её судьба — штурм, где либо найдётся подтверждение, либо она уедет в
`REJECTED.md`.
- **Уже починено по ходу ревью** → **ничего**. Починенное не заводим.
- **Развилка, решённая при ревью** → ничего; решённая «потом» → задача с
вопросом в разделе «Вопросы» и тегом `question`.
## Порядок
1. **Возьми выход триажа, а не сырые находки.** Сырой отчёт — это симптомы до
дедупликации; в нём одна причина размазана по нескольким строкам.
2. **Кластеризуй по причине.** Пять находок об одном отсутствующем инварианте —
одна задача, а не пять. Класс мелочи (nits, косметика) — **один пакетный
файл** со списком пунктов, а не файл на каждую запятую.
3. **Дедуп против живых задач и `REJECTED.md`.** Аудит переоткрывает уже
заведённое и уже выкинутое. Нашлось среди живых — дописываем находку в
существующий файл. Нашлось в `REJECTED.md` — это сигнал: причина отказа могла
устареть, выноси пользователю, а не заводи молча заново.
4. **Разложи по целям.** У каждой заводимой задачи должен быть `goal:<слаг>`.
Половина находок ревью не служит ничему из линии продукта — их цель это
**куст** («прочность слияния», «журнал и пересборка», «наблюдаемость»).
Подходящего куста нет — заведи его целью (`add --type goal --section кусты`)
в том же проходе: без цели задача не попадёт ни в один спринт, а значит не
будет сделана никогда.
5. **Покажи карту до создания файлов.** Кластер → задача / идея / строка в
пакетный файл / уже заведено / отброшено, и под какую цель — пачкой через
`AskUserQuestion`. Это тот же барьер, что и «три кандидата» в интейке из
диалога: массовое заведение файлов без подтверждения — ровно тот отказ, ради
которого интейк из ревью и выделен. Дешёвая мелочь по явному согласию может
заводиться и без поштучного вопроса — но карта пользователю предъявляется
всё равно.
6. **Заводи утверждённое** через `tasks.py add`, с двумя добавками:
- **тег партии** — `--tag review-ГГГГ-ММ-ДД` (или `audit-<тема>`), чтобы весь
заход разбора поднимался одной командой `list --tag …`;
- **провенанс в теле** — кто нашёл, каким проходом, с каким свидетельством.
Без него через месяц не отличить проверенную находку от догадки.
7. `tasks.py check`.
## Куда девается серьёзность, если приоритетов нет
Приоритетов нет, и отображать серьёзность некуда — но **выкидывать её нельзя**.
Правило замены:
- **тяжёлая находка со свидетельством** → задача под ту цель, которой она
угрожает, и **кандидат в ближайший набор**: серьёзность здесь превращается в
довод при выборе цели следующего спринта, а не в уровень в файле. Довод
записывается причиной в мета-строке (`--reason`), иначе к моменту набора его
никто не вспомнит;
- **находка, ломающая уже идущий спринт**, — не интейк вовсе: см. правило
вторжения в скилле `session`. В беклог она падает, только если врываться не
положено;
- **низкая уверенность или нет свидетельства** → идея;
- **мелочь** → строка в пакетный файл;
- **уже починено / развилка решена сейчас** → ничего.
Словарей серьёзности много, и отображать их механически не на что: при сомнении
— вопрос пользователю, а не догадка.
## Поимённая сверка
Интейк считается выполненным, только если **каждая** находка триажа получила
исход: слаг заведённой задачи, ссылку на существующую, строку пакетного файла
или запись «не заведена: причина». Нулевой урожай при непустом отчёте триажа
виден сразу — и это единственный способ отличить «находок не было» от «не стал
заводить». Список составляет не тот, кто отчитывается о заведении.
Границы покрытия отчёта — то, что ревью проверить **не смогло**, — не находки и
в задачи не идут: у них нет предмета. Их место в докладе, не в беклоге.
## Доклад
- Источник (какое ревью/аудит, сколько находок на входе).
- Свёрнуто в задачи: N кластеров из M находок, со слагами, целями и тегом партии.
- Что не заведено и почему: починено инлайн, уже заведено, ушло в идеи, в
`REJECTED.md`.
- Поимённая сверка: находок на входе N, исход есть у N.
- `tasks.py check`.
@@ -0,0 +1,82 @@
# Декомпозиция и мозговой штурм
Обе операции превращают одну запись в несколько (или в ноль). Разница во входе:
декомпозиция дробит **готовую задачу или эпик**, штурм прорабатывает **идею**,
которая ещё не задача.
## Тест декомпозиции
Задачу можно дробить, только если части удовлетворяют **обоим** условиям:
1. **Мерджатся независимо.** Часть Б не требует, чтобы часть А была уже влита.
Есть порядок «сперва А, потом Б, иначе не собрать» → это не декомпозиция, а
план реализации: шаги остаются **внутри одного файла**.
2. **Каждая даёт видимую пользу.** Часть, полезная только в комплекте с другой,
— не самостоятельная задача. Пользу проверяй тестом «готова к взятию»
(task-format): что станет наблюдаемо иначе именно от этой части и какие у неё
собственные критерии приёмки.
Не проходит хотя бы одно — **не дроби**. Ложная декомпозиция плодит файлы,
которые нельзя взять поодиночке, и переоценка потом склеивает их обратно.
**Цель наследуется.** Все части несут `goal:` родителя: декомпозиция не меняет
того, чему работа служит. Если у части цель другая — это признак, что дробили не
по той границе, либо что часть вообще из другой работы.
## Что делать с родителем
После разделения родитель **не остаётся** третьей висящей строкой:
- части полностью замещают его → `close <slug> --reason "разложена на a, b"`.
`REJECTED.md` здесь — не «выкинули», а именно тот след, что переживает запись:
через квартал вопрос «куда делась задача X» отвечается строкой со ссылками на
наследников, а не археологией git;
- родитель осмыслен как зонтик → `edit <slug> --type epic`, тело — ссылки на
задачи-части, своих шагов у него нет. **Эпик не берётся в спринт** и живёт
ровно до тех пор, пока не закрыта последняя часть.
Зонтик, который перестал быть временным и описывает направление, а не работу, —
это уже **цель**, а не эпик. Тип на месте не меняется (цель живёт в другом
индексе): заводится `[goal]` в `PLAN.md`, задачи получают `--goal <новый слаг>`,
эпик закрывается с причиной-ссылкой.
## Когда декомпозиция случается посреди спринта
Задача, которая **переросла в эпик**, распознаётся до того, как под неё заведено
предложение об изменении: иначе его придётся выбрасывать. Она помечается
`[epic]`, выходит из набора (`sprint drop … --reason "переросла в эпик"`), уходит
на декомпозицию, а спринт продолжается остальными. Части заводятся сразу, но в
текущий набор **не добавляются** — набор заморожен.
## Мозговой штурм идеи
Идея (`[idea]`) не проходит тест «готова к взятию»: неясно, что именно делаем.
Штурм проясняет — и это **generative-операция, а не applicative**.
Applicative-штурм («перечисли задачи, следующие из идеи») выдаёт очевидное:
перечисляется то, что уже видно в формулировке. Ценное — на уровень выше.
1. **Сперва — формы, а не задачи.** Предложи **три разные постановки** идеи и
назови **компромисс каждой**: что она даёт, чем платит, что оставляет за
бортом. Если получилась одна постановка — штурм не состоялся, это
applicative.
2. **Вынеси формы пользователю** через `AskUserQuestion` с компромиссами. Рамку
выбирает он: это продуктовое решение, не механика.
3. **Назови цель.** Выбранная форма служит какой-то цели — существующей или
новой. Идея, для которой цель не находится, скорее всего уезжает в
`REJECTED.md`, а не заводится задачей.
4. **Только выбранную форму** дроби по тесту декомпозиции выше и проставь
критерии приёмки: без них наследники останутся идеями под другим именем.
**«Выкинуть» — полноправный исход штурма, а не его неудача.** Проработка, честно
показавшая, что пользы нет или она несоразмерна цене, — это результат: идея
уезжает с этой самой причиной, и та причина гасит её повторное появление.
## Доклад
- Идея/задача на входе, выбранная рамка (для штурма), задачи-наследники со
слагами, целями и секциями.
- Судьба родителя: удалён / стал эпиком / стал целью / выкинут с причиной.
- `tasks.py check` после правок.
- Границы покрытия: какие постановки рассмотрены и какие сознательно отброшены —
чтобы штурм не пришлось повторять с нуля.
@@ -0,0 +1,203 @@
# Формат задач, целей и индексов
Заголовок, мета-строку и строку индекса ставит `tasks.py add` — руками их не
пишут. Этот файл описывает, что именно скрипт создаёт и что проверяет `check`;
тело задачи (одну фразу, критерии, рамки, контекст) дописывает агент.
## Файл задачи
`items/<slug>.md`:
```markdown
# Тай-брейк при равной полноте
**Секция:** ядро — вышла из спринта: остаток писал нерешённое в журнал · **Теги:** goal:merge-robustness, sprint:2026-08
При столкновении точек выигрывает более полная, но при равной полноте побеждает
последняя доставка — а она систематически беднее первой.
## Критерии приёмки
- повторный прогон свёртки даёт тот же отпечаток состояния — оракул: команда сверки
- накопительная метрика за сутки не уменьшается после повторной доставки — оракул: тест
- в логе видно, какая из двух точек выиграла и почему — оракул: глазами по логу прогона
## Рамки
Схема не трогается; данные только читаются; перезапуск сервиса допустим.
Связано: решение о канонической форме содержимого.
```
- **Заголовок H1** — он же заголовок строки в индексе, дословно. Тип кодируется
префиксом `[goal]` / `[idea]` / `[epic]`; обычная задача — без префикса.
Отдельного поля типа **нет**: два места для одного факта разъезжаются, а
префикс виден прямо в индексе, где и принимается решение «брать или не брать».
- **Мета-строка** — первая непустая строка после заголовка. Обязательна секция,
причина после тире желательна (именно она объясняет, почему задача здесь
оказалась — в том числе «вышла из спринта: …»), теги опциональны. Поля
разделяются ` · `, порядок свободный. `·` — служебный разделитель: в тексте
причины его быть не должно.
- **Тело** — одна фраза «что станет наблюдаемо иначе», критерии приёмки, рамки,
контекст, ссылки. Пишется на языке документации проекта.
Тело — не план реализации и не спецификация: принятое и реализованное переезжает
в документацию проекта, а файл задачи удаляется.
### Критерии приёмки
2–5 проверяемых утверждений, **у каждого назван оракул**. Не «работает
корректно», а «повторный прогон даёт тот же отпечаток — оракул: команда сверки».
Это не второе определение готовности, а проектная конкретизация вопроса «по чему
видно, что закончено» из теста готовности ниже: там сказано «признак
завершённости», здесь — «признак плюс чем проверяется».
**У идей критериев нет — именно поэтому они идеи.**
**Критерии — пол, но расхождение с ними есть дефект критериев.** Если приёмщик
видит, что критерии закрыты, а суть задачи не достигнута, он **правит критерии и
возвращает задачу исполнителю**, а не держит невидимое сверх-требование. Иначе
исполнитель никогда не знает, закончил ли, и мотивирован занижать критерии
заранее.
### Рамки
Одна строка: чего касаться нельзя, что перезапускается, что считается
необратимым, трогается ли схема данных. **Свойства репозитория сюда не пишутся**
— номер последней миграции, версия зависимости, хеш: в лежалой задаче они
протухают молча и становятся ложной рамкой. Снимок берётся при постановке, а не
при заведении.
### Вопросы
Неразобранное решение человека живёт разделом `## Вопросы` **плюс тегом
`question`**. Тег — то, по чему вопрос виден снаружи файла (`list --questions`) и
чем работает правило «задача с открытым вопросом в набор не берётся». Раздел без
тега или тег без раздела — дрейф, `check` о нём скажет.
Ответ записывается в тело, тег снимается `edit <slug> --rm-tag question`, а хук
переписывается: «Решено: …» на вопрос «почему это лежит в беклоге» уже не
отвечает.
## Файл цели
```markdown
# [goal] Прочность слияния
**Секция:** кусты · **Теги:** decomposed
Ради чего: точки из разных доставок сходятся в один часовой объект, и сегодня
исход столкновения зависит от порядка доставки, а не от содержания.
## Завершение
Достигнута, когда исход слияния не зависит ни от порядка, ни от времени
доставки, и это подтверждено повторным прогоном на живом корпусе.
```
- **Задачи цели здесь не перечисляются.** Перечень даёт
`tasks.py list --goal <слаг>`; хранимый список стал бы третьим индексом и
поехал бы на первой же закрытой задаче.
- **Раздел «Завершение»** — то, по чему видно, что цель достигнута. Он же
отличает «цель ещё не декомпозирована» от «все её задачи закрыты»: пометка
вроде тега `decomposed` или строки в теле ставится, когда цель разложена на
задачи.
- Цель живёт в `PLAN.md` и **никогда** — в `BACKLOG.md` или `SPRINT.md`.
## Слаг
Латиница и цифры, kebab-case, без ведущих, хвостовых и двойных дефисов
(`foo-bar`, не `-foo`, `a--b`). Именуется **по сути, а не по текущей
формулировке**: заголовок будет переписан при переоценке, а слаг стоит в ссылках
из других задач, коммитов и черновиков. **Транслита не заводим**
`tie-break-equal-completeness`, а не `taj-brejk-pri-ravnoj-polnote`: транслит
нечитаем для того, кто ищет по смыслу, и не сокращается.
Переименование слага — не правка, а перенос ссылок: делается одним атомарным
проходом по всем местам, где слаг упомянут, иначе останутся битые ссылки,
которых никто не проверяет.
## Индексы
Строка везде одной формы:
```markdown
- [Заголовок дословно](items/slug.md) — хук
```
Хук отвечает на «почему это лежит в беклоге» одним предложением: состояние,
остаток, боль. Пересказ первого абзаца бесполезен — он уже есть по ссылке.
| Файл | Что отвечает | Секции |
| --- | --- | --- |
| `PLAN.md` | какие есть цели, в каком порядке идёт линия и почему | линия (упорядоченная) и кусты |
| `BACKLOG.md` | что **можно взять** — только задачи | секции проекта (по умолчанию ядро/инфра) |
| `SPRINT.md` | какая цель и какой набор под неё | одна: «Набор» |
| `REJECTED.md` | что ушло без реализации и почему | — |
Секции — **единственные заголовки `##` в индексе**: любой другой `##` в
преамбуле проверка сочтёт секцией. Внутри секции беклога порядок значения не
имеет — порядка в беклоге нет вовсе. В **линии** плана порядок значим и
обосновывается прозой; двигают строку `move <slug> --section линия --after
<другой>`.
Индексы **производны**: расходятся с файлом — правим индексы (`check --fix`).
Строку руками не пишут.
`SPRINT.md` и есть артефакт заморозки: без него набор существует только в
контексте сессии, и нарушение заморозки ненаблюдаемо.
## `REJECTED.md`
Туда уходит задача, покинувшая беклог **без реализации**. Строку пишет
`tasks.py close --reason`, а `check` следит за форматом:
```markdown
- 2026-07-23 `versii-kachestvo-repaki` — Версии и качество одного тайтла.
Причина: калибровка болей — не боль, ни разу не возникло за полгода.
Была секция: инфра.
```
Реализованные сюда не попадают: у них остаётся коммит и документация. У
выкинутой не остаётся ничего — и через квартал она возвращается тем же текстом.
Это первое место, куда смотрит дедупликация при заведении.
Запись не запрещает завести задачу заново: изменился контекст — заводим и
ссылаемся на строку, объясняя, что изменилось.
## Теги
Единственный механизм разметки, потому что `list --tag` уже умеет отбирать по
ним порцию разбора. Отдельных полей мета-строки под это не заводим.
- `goal:<слаг>` — цель, которой служит задача. Обязателен: задача без цели не
попадёт ни в один спринт.
- `question` — в файле есть неразобранный раздел «Вопросы».
- `sprint:<слаг>` — задача заведена в этом спринте; по нему отбирается первая
порция разбора («урожай спринта»).
Свои теги проект заводит свободно (партия ревью `review-ГГГГ-ММ-ДД`, тема,
источник) — словарь не фиксирован. В индексы теги не выносим: индексы
производны, отбор делает `list --tag`, а не глаза.
## Тест «готова к взятию»
Задача готова, если из файла отвечаются три вопроса:
1. **Что станет наблюдаемо иначе**, когда она сделана — снаружи: пользователю,
владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет
ломаться Y при Z» — ответ.
2. **По чему видно, что закончено** — критерии приёмки с оракулами.
3. **Какой цели она служит** — тег `goal:` и одна строка «почему именно этой».
Не отвечается первый или второй вопрос → это **идея** (`[idea]`), её место в
штурме. Не отвечается третий → либо цель есть и не проставлена, либо задача не
служит ничему — тогда её не надо заводить.
Отвечается всё, но задача не делается одним заходом и не мерджится целиком →
**эпик** (`[epic]`), сперва декомпозиция. Эпик временен и исчезает после
разбора; цель (`[goal]`) постоянна — не путать.
Тест применяется при заведении и при переоценке. К старым задачам, которых
операция не касается, задним числом не применяется — беклог не переоформляют
«заодно».
File diff suppressed because it is too large Load Diff