Files
dev-skills/av-dev-tasks/skills/tasks/references/task-format.md
T
av 20dca29add av-dev-tasks: починены находки ревью, добавлена адаптация чужого репозитория
- атомарность: sprint drop и move собирают план правок целиком и пишут
  одним проходом; раньше отказ на втором слаге оставлял первый файл
  переписанным при нетронутом индексе
- хук переехал в мета-строку файла: индекс стал производным, и check --fix
  больше не теряет текст, восстанавливая строку
- механизировано то, что было записано, но не проверялось: слаг спринта и
  автотег, отказ по факту непустого раздела вопросов, число критериев,
  пометка decomposed, покрытие причин
- reopen возвращает закрытую задачу: без него порядок «пайплайн доложил →
  приёмщик судит → владелец закрывает» был односторонним
- скилл adopt: приходит в чужой репозиторий и выводит заполненный каталог
  задач. На копии беклога healthlog — 12 целей, 38 задач, 36 переименований,
  86 ссылок в 36 файлах, check зелёный
2026-08-03 11:45:23 +03:00

246 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Формат задач, целей и индексов
Заголовок, мета-строку и строку индекса ставит `tasks.py add` — руками их не
пишут. Этот файл описывает, что именно скрипт создаёт и что проверяет `check`;
тело задачи (одну фразу, критерии, рамки, контекст) дописывает агент.
## Файл задачи
`items/<slug>.md`:
```markdown
# Тай-брейк при равной полноте
**Секция:** ядро — вышла из спринта: остаток писал нерешённое в журнал · **Хук:** порядок канонических форм берёт меньшее в 96% случаев — для накопительных это систематический недосчёт · **Теги:** goal:merge-robustness, sprint:2026-08-03
При столкновении точек выигрывает более полная, но при равной полноте побеждает
последняя доставка — а она систематически беднее первой.
## Критерии приёмки
- повторный прогон свёртки даёт тот же отпечаток состояния — оракул: команда сверки
- накопительная метрика за сутки не уменьшается после повторной доставки — оракул: тест
- в логе видно, какая из двух точек выиграла и почему — оракул: глазами по логу прогона
## Рамки
Схема не трогается; данные только читаются; перезапуск сервиса допустим.
Связано: решение о канонической форме содержимого.
```
- **Заголовок H1** — он же заголовок строки в индексе, дословно. Тип кодируется
префиксом `[goal]` / `[idea]` / `[epic]`; обычная задача — без префикса.
Отдельного поля типа **нет**: два места для одного факта разъезжаются, а
префикс виден прямо в индексе, где и принимается решение «брать или не брать».
- **Мета-строка** — первая непустая строка после заголовка. Обязательна секция,
причина после тире желательна (именно она объясняет, почему задача здесь
оказалась — в том числе «вышла из спринта: …»), хук и теги опциональны. Поля
разделяются ` · `, порядок свободный. `·` — служебный разделитель: в тексте
причины и хука его быть не должно.
- **Хук живёт здесь, а не только в индексе.** Строка индекса его повторяет и
производна от него: `check` сверяет, `check --fix` восстанавливает пропавшую
строку **вместе с хуком**. Пока хук лежал только в индексе, штатная починка
дрейфа теряла его молча и навсегда — а хук это единственное, по чему задачу
выбирают, не открывая.
- **Тело** — одна фраза «что станет наблюдаемо иначе», критерии приёмки, рамки,
контекст, ссылки. Пишется на языке документации проекта.
Тело — не план реализации и не спецификация: принятое и реализованное переезжает
в документацию проекта, а файл задачи удаляется.
### Критерии приёмки
2–5 проверяемых утверждений **списком** `- …`, **у каждого назван оракул**. Не
«работает корректно», а «повторный прогон даёт тот же отпечаток — оракул:
команда сверки». Это не второе определение готовности, а проектная
конкретизация вопроса «по чему видно, что закончено» из теста готовности ниже:
там сказано «признак завершённости», здесь — «признак плюс чем проверяется».
**Что из этого механизировано.** `check` и `sprint take` считают пункты: меньше
двух — отказ («— работает» одной строкой больше не проходит), больше пяти —
замечание, обычно это признак, что задача крупнее задачи. Наличие оракула
проверяется **эвристикой** — словом «оракул» в пункте, — и потому даёт только
замечание: настоящий оракул от слова «оракул» машина не отличает, и делать вид,
что проверено больше проверенного, хуже, чем не проверять вовсе.
**У идей критериев нет — именно поэтому они идеи.**
**Критерии — пол, но расхождение с ними есть дефект критериев.** Если приёмщик
видит, что критерии закрыты, а суть задачи не достигнута, он **правит критерии и
возвращает задачу исполнителю**, а не держит невидимое сверх-требование. Иначе
исполнитель никогда не знает, закончил ли, и мотивирован занижать критерии
заранее.
### Рамки
Одна строка: чего касаться нельзя, что перезапускается, что считается
необратимым, трогается ли схема данных. **Свойства репозитория сюда не пишутся**
— номер последней миграции, версия зависимости, хеш: в лежалой задаче они
протухают молча и становятся ложной рамкой. Снимок берётся при постановке, а не
при заведении.
### Вопросы
Неразобранное решение человека живёт разделом `## Вопросы` **плюс тегом
`question`**. Раздел без тега или тег без раздела — дрейф, `check` о нём скажет.
**Судит факт, а не метка.** Отказ во взятии даёт **непустой раздел «Вопросы»**,
независимо от того, стоит ли тег: иначе забывший тег проходил бы, а поставивший
спотыкался — стимул ровно обратный записанному правилу. Тег производен: он нужен
отбору снаружи файла (`list --questions`, `list --tag question`), и его
отсутствие при непустом разделе — замечание, а не лазейка. Тег без раздела тоже
отказ, но с другим советом: либо вопрос записан не туда, либо тег пора снять.
Ответ записывается в тело, тег снимается `edit <slug> --rm-tag question`, а хук
переписывается: «Решено: …» на вопрос «почему это лежит в беклоге» уже не
отвечает.
## Файл цели
```markdown
# [goal] Прочность слияния
**Секция:** кусты · **Теги:** decomposed
Ради чего: точки из разных доставок сходятся в один часовой объект, и сегодня
исход столкновения зависит от порядка доставки, а не от содержания.
## Завершение
Достигнута, когда исход слияния не зависит ни от порядка, ни от времени
доставки, и это подтверждено повторным прогоном на живом корпусе.
```
- **Задачи цели здесь не перечисляются.** Перечень даёт
`tasks.py list --goal <слаг>`; хранимый список стал бы третьим индексом и
поехал бы на первой же закрытой задаче.
- **Раздел «Завершение»** — то, по чему видно, что цель достигнута.
- **Тег `decomposed`** отличает «цель ещё не разобрана» от «все её задачи
закрыты» — два состояния, у которых снаружи один и тот же признак: задач нет.
Пометка именно **тегом**, а не строкой в теле: только так она проверяется.
`check` требует его у цели без задач, `check --fix` сам ставит его цели, у
которой задачи есть, а цель с тегом и без задач — прямое приглашение закрыть.
- Цель живёт в `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` | что ушло без реализации и почему | — |
Секции — **единственные заголовки `##` в индексе**: любой другой `##` в
преамбуле проверка сочтёт секцией. Внутри секции беклога порядок значения не
имеет — порядка в беклоге нет вовсе.
**Секции «блокеры» среди них нет.** Блокер — состояние, а не полка: он живёт до
ответа человека, а следы остаются вопросами в файлах задач распущенного спринта.
Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила
бы правилу «эскалируем немедленно», поэтому `init` её не заводит, а `check`
говорит о ней в чужом беклоге. Переезжаешь с такой секцией — удали её. В **линии** плана порядок значим и
обосновывается прозой; двигают строку `move <slug> --section линия --after
<другой>`.
Индексы **производны**: расходятся с файлом — правим индексы (`check --fix`).
Строку руками не пишут.
Отсюда же ответ на «а если оборвётся посередине». Мутация сперва проверяет всё
и складывает правки, и только потом пишет: сначала все временные файлы, потом
переименования подряд. Полной транзакции на несколько файлов файловая система не
даёт, но окно сжато до цепочки переименований, а **всё, что в нём может
разъехаться, — производное**: файлы целы, индексы восстанавливает `check --fix`.
Поэтому отказ на второй задаче из пяти не оставляет первую переписанной при
нетронутых индексах.
`SPRINT.md` и есть артефакт заморозки: без него набор существует только в
контексте сессии, и нарушение заморозки ненаблюдаемо.
## `REJECTED.md`
Туда уходит задача, покинувшая беклог **без реализации**. Строку пишет
`tasks.py close --reason`, а `check` следит за форматом:
```markdown
- 2026-07-23 `versii-kachestvo-repaki` — Версии и качество одного тайтла.
Причина: калибровка болей — не боль, ни разу не возникло за полгода.
Была секция: инфра.
```
Реализованные сюда не попадают: у них остаётся коммит и документация. У
выкинутой не остаётся ничего — и через квартал она возвращается тем же текстом.
Это первое место, куда смотрит дедупликация при заведении.
Запись не запрещает завести задачу заново: изменился контекст — заводим и
ссылаемся на строку, объясняя, что изменилось.
## Теги
Единственный механизм разметки, потому что `list --tag` уже умеет отбирать по
ним порцию разбора. Отдельных полей мета-строки под это не заводим.
- `goal:<слаг>` — цель, которой служит задача. Обязателен: задача без цели не
попадёт ни в один спринт.
- `question` — в файле есть неразобранный раздел «Вопросы».
- `sprint:<слаг>` — задача заведена в этом спринте; по нему отбирается первая
порция разбора («урожай спринта»). **Ставится сам**: слаг спринта заводит
`sprint start` (по умолчанию — дата начала, он же пишется в `SPRINT.md`), и
`add` при открытом спринте помечает заводимое. Тег, который надо помнить
ставить руками, не ставится никогда — а на нём висит правило «первая порция
разбора — урожай прошедшего спринта».
- `decomposed` — на цели: разложена на задачи (см. «Файл цели»).
Отбор — `list --tag a,b`: перечисленные через запятую теги требуются **все
сразу** (это И, не ИЛИ). Тег, которого нет ни у одной задачи, `list` называет
вслух: молчаливый ноль читается как «таких задач нет», а чаще это опечатка.
Свои теги проект заводит свободно (партия ревью `review-ГГГГ-ММ-ДД`, тема,
источник) — словарь не фиксирован. В индексы теги не выносим: индексы
производны, отбор делает `list --tag`, а не глаза.
## Тест «готова к взятию»
Задача готова, если из файла отвечаются три вопроса:
1. **Что станет наблюдаемо иначе**, когда она сделана — снаружи: пользователю,
владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет
ломаться Y при Z» — ответ.
2. **По чему видно, что закончено** — критерии приёмки с оракулами.
3. **Какой цели она служит** — тег `goal:` и одна строка «почему именно этой».
Не отвечается первый или второй вопрос → это **идея** (`[idea]`), её место в
штурме. Не отвечается третий → либо цель есть и не проставлена, либо задача не
служит ничему — тогда её не надо заводить.
Отвечается всё, но задача не делается одним заходом и не мерджится целиком →
**эпик** (`[epic]`), сперва декомпозиция. Эпик временен и исчезает после
разбора; цель (`[goal]`) постоянна — не путать.
Тест применяется при заведении и при переоценке. К старым задачам, которых
операция не касается, задним числом не применяется — беклог не переоформляют
«заодно».