Files
dev-skills/av-dev-pm/skills/tasks/references/task-format.md
T
avandClaude Opus 5 b99c0c2366 канон версии 3: роадмап, род работы, границы задачи
Три изменения одной версией, потому что все три про одно — можно ли
оценить задачу, не открывая код.

PLAN.md → ROADMAP.md. Слово «план» значило в репозитории три разных вещи:
оглавление целей, план реализации внутри задачи и PLAN.json разовой
адаптации. Переименовано целиком — ключ конфига tasks.plan → tasks.roadmap,
--index roadmap, --roadmap-sections, --roadmap. Старый ключ в docs/.pm.json
не игнорируется молча: скрипт останавливается кодом 3 и называет
переименование, иначе проект искал бы опечатку там, где на самом деле
версия канона.

Род работы — тег kind:feature|fix|chore|research, вторая ось поверх типа
записи. В один префикс их не свести: идея бывает про функцию, эпик функцией
и является. Дом — тег, потому что теги здесь единственный механизм
разметки, а list --kind работает даром; цена принята — в строку индекса род
не попадает. Словарь закрыт, иначе он разъедется на bug/bugfix/fix/defect.
Отдельно легализован chore: у него «что станет наблюдаемо иначе» отвечается
разработчику, а раньше такие задачи либо не заводились, либо придумывали
себе пользовательскую пользу — и это второе хуже, оно проходит проверку.

Раздел «Затрагивает» — границы, которых изменение касается: эндпоинт,
таблица и миграция, формат на диске, публичный тип пакета. Без него задача
оценивается по объёму текста, а не по объёму поверхности. Механизируется
только наличие непустого раздела: полноту перечня машина не видит.

Род и границы требуются к взятию в спринт, а не к заведению — тот же приём,
что уже работает для критериев приёмки, и по той же причине. check о
пропаже только напоминает: иначе два живых проекта покраснели бы на 98
задачах, заведённых до этого решения.

Плюс правила языка задач: англицизм, у которого есть русское слово,
заменяется; термин не из паспорта, архитектуры или конвенций вводится
строкой или не употребляется; задача, которую не удаётся сказать просто,
чаще всего не одна задача.

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

26 KiB
Raw Blame History

Формат задач, целей и индексов

Заголовок, мета-блок и строку индекса ставит tasks.py add — руками их не пишут. Этот файл описывает, что именно скрипт создаёт и что проверяет check; тело задачи (одну фразу, критерии, рамки, контекст) дописывает агент.

Файл задачи

items/<slug>.md:

# Тай-брейк при равной полноте

- **Секция:** ядро — вышла из спринта: остаток писал нерешённое в журнал
- **Зачем:** порядок канонических форм берёт меньшее в 96% случаев — для накопительных это систематический недосчёт
- **Теги:** goal:merge-robustness, kind:fix, sprint:2026-08-03

При столкновении точек выигрывает более полная, но при равной полноте побеждает
последняя доставка — а она систематически беднее первой.

## Затрагивает

Таблица `points` и её миграция; правило слияния в приёме доставки; формат
отпечатка состояния на диске. Публичного контракта не трогает.

## Критерии приёмки

- повторный прогон свёртки даёт тот же отпечаток состояния — оракул: команда сверки
- накопительная метрика за сутки не уменьшается после повторной доставки — оракул: тест
- в логе видно, какая из двух точек выиграла и почему — оракул: глазами по логу прогона

## Рамки

Схема не трогается; данные только читаются; перезапуск сервиса допустим.

Связано: решение о канонической форме содержимого.
  • Заголовок H1 — он же заголовок строки в индексе, дословно. Тип кодируется префиксом [goal] / [idea] / [epic]; обычная задача — без префикса. Отдельного поля типа нет: два места для одного факта разъезжаются, а префикс виден прямо в индексе, где и принимается решение «брать или не брать».

  • Мета-блок — список сразу после заголовка, поле на строку. Обязательна секция, причина после тире желательна (именно она объясняет, почему задача здесь оказалась — в том числе «вышла из спринта: …»), «зачем» и теги опциональны. Порядок свободный, поле в одну строку. Нераспознанные поля сохраняются: скрипт правит свои и не трогает чужие.

  • «Зачем» отвечает на «зачем нужна эта задача» — состояние, остаток, боль. Не пересказ задачи: пересказ уже есть по ссылке. Живёт здесь, а не только в индексе: строка индекса его повторяет и производна от него, check сверяет, check --fix восстанавливает пропавшую строку вместе с ним. Пока поле лежало только в индексе, штатная починка дрейфа теряла его молча и навсегда — а это единственное, по чему задачу выбирают, не открывая.

  • Тело — одна фраза «что станет наблюдаемо иначе», затрагиваемые границы, критерии приёмки, рамки, контекст, ссылки. Пишется на языке документации проекта: предметно, без англицизмов, у которых есть русское слово, и без терминов, которых нет ни в паспорте, ни в архитектуре, ни в конвенциях (правило и его причина — в SKILL.md, раздел «Как написана задача»).

Мета одной строкой через · — прежняя форма. Она читается по-прежнему, check называет её дрейфом, check --fix переписывает списком; поле Хук при этом становится Зачем. Причина отказа от строки простая: с тремя полями и длинным «зачем» строка уезжала за экран, а · приходилось запрещать в тексте причины и самого «зачем».

Тело — не план реализации и не спецификация: принятое и реализованное переезжает в документацию проекта, а файл задачи удаляется.

Затрагивает

Перечень границ, которых изменение касается. Границей считается то, у чего есть внешняя сторона и цена изменения:

  • эндпоинт, команда, форма ответа, код ответа;
  • таблица, поле, миграция, формат на диске, формат сообщения в очереди;
  • публичный тип или функция пакета, конфиг и его образцы;
  • внешний сервис или библиотека, чьё поведение становится нужным.

Ничего из этого не трогается — так и пишется: «границ не трогает, изменение внутри одного узла». Это ответ, а не пустой раздел.

Границы, а не замысел. «Переписать хранилище на новый драйвер» — замысел; таблица points и её миграция, эндпоинт POST /ingest — границы. Разница проверяется вопросом «это можно назвать до того, как решено как делать?»: если нет, строка описывает реализацию, и её место в предложении об изменении.

Свойства репозитория сюда не пишутся — по той же причине, что и в рамки: имя таблицы стабильно, номер последней миграции протухает молча. Пишется таблица points и её миграция, а не миграция 0042.

Что из этого механизировано. check и sprint take смотрят только на наличие непустого раздела. Полнота перечня машине не видна: границу, которую забыли назвать, она от отсутствующей не отличает. Раздела нет — отказ во взятии: оценивать нечем.

У идей раздела нет — как и критериев: границы становятся известны, когда идея превращается в задачу.

Критерии приёмки

2–5 проверяемых утверждений списком - …, у каждого назван оракул. Не «работает корректно», а «повторный прогон даёт тот же отпечаток — оракул: команда сверки». Это не второе определение готовности, а проектная конкретизация вопроса «по чему видно, что закончено» из теста готовности ниже: там сказано «признак завершённости», здесь — «признак плюс чем проверяется».

Что из этого механизировано. check и sprint take считают пункты: меньше двух — отказ («— работает» одной строкой больше не проходит), больше пяти — замечание, обычно это признак, что задача крупнее задачи. Наличие оракула проверяется эвристикой — словом «оракул» в пункте, — и потому даёт только замечание: настоящий оракул от слова «оракул» машина не отличает, и делать вид, что проверено больше проверенного, хуже, чем не проверять вовсе.

У идей критериев нет — именно поэтому они идеи.

Критерии — пол, но расхождение с ними есть дефект критериев. Если приёмщик видит, что критерии закрыты, а суть задачи не достигнута, он правит критерии и возвращает задачу исполнителю, а не держит невидимое сверх-требование. Иначе исполнитель никогда не знает, закончил ли, и мотивирован занижать критерии заранее.

Рамки

Одна строка: чего касаться нельзя, что перезапускается, что считается необратимым, трогается ли схема данных. Свойства репозитория сюда не пишутся — номер последней миграции, версия зависимости, хеш: в лежалой задаче они протухают молча и становятся ложной рамкой. Снимок берётся при постановке, а не при заведении.

Вопросы

Неразобранное решение человека живёт разделом ## Вопросы плюс тегом question. Раздел без тега или тег без раздела — дрейф, check о нём скажет.

Судит факт, а не метка. Отказ во взятии даёт непустой раздел «Вопросы», независимо от того, стоит ли тег: иначе забывший тег проходил бы, а поставивший спотыкался — стимул ровно обратный записанному правилу. Тег производен: он нужен отбору снаружи файла (list --questions, list --tag question), и его отсутствие при непустом разделе — замечание, а не лазейка. Тег без раздела тоже отказ, но с другим советом: либо вопрос записан не туда, либо тег пора снять.

Ответ на вопрос — три правки, и первая обязательна. Раздел «Вопросы» опустошается: ответ переезжает в тело решением, а не остаётся вопросом рядом с ответом. Затем снимается тег (edit <slug> --rm-tag question) и переписывается «зачем»: «Решено: …» на вопрос «зачем нужна эта задача» уже не отвечает.

Порядок именно такой, потому что судит раздел, а не тег. sprint take смотрит в непустой раздел и откажет взять задачу даже со снятым тегом, а check на снятый тег при непустом разделе посоветует тег вернуть. Снять тег, не опустошив раздел, — значит закольцевать себя между двумя советами.

Файл цели

# [goal] Прочность слияния

- **Секция:** темы
- **Теги:** decomposed

Ради чего: точки из разных доставок сходятся в один часовой объект, и сегодня
исход столкновения зависит от порядка доставки, а не от содержания.

## Завершение

Достигнута, когда исход слияния не зависит ни от порядка, ни от времени
доставки, и это подтверждено повторным прогоном на живом корпусе.
  • Задачи цели здесь не перечисляются. Перечень даёт tasks.py list --goal <слаг>; хранимый список стал бы третьим индексом и поехал бы на первой же закрытой задаче.
  • Раздел «Завершение» — то, по чему видно, что цель достигнута.
  • Тег decomposed отличает «цель ещё не разобрана» от «все её задачи закрыты» — два состояния, у которых снаружи один и тот же признак: задач нет. Пометка именно тегом, а не строкой в теле: только так она проверяется. check напоминает о нём у цели без задач замечанием — неразобранная цель законна и зелёного прогона не ломает; check --fix сам ставит его цели, у которой задачи есть, а цель с тегом и без задач — прямое приглашение закрыть.
  • Цель живёт в ROADMAP.md и никогда — в BACKLOG.md или SPRINT.md.

Слаг

Латиница и цифры, kebab-case, без ведущих, хвостовых и двойных дефисов (foo-bar, не -foo, a--b). Именуется по сути, а не по текущей формулировке: заголовок будет переписан при переоценке, а слаг стоит в ссылках из других задач, коммитов и черновиков. Транслита не заводимtie-break-equal-completeness, а не taj-brejk-pri-ravnoj-polnote: транслит нечитаем для того, кто ищет по смыслу, и не сокращается.

Переименование слага — не правка, а перенос ссылок: делается одним атомарным проходом по всем местам, где слаг упомянут, иначе останутся битые ссылки, которых никто не проверяет.

Индексы

Строка везде одной формы:

- [Заголовок дословно](items/slug.md) — зачем

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

Файл Что отвечает Секции
ROADMAP.md какие есть цели, в какой очереди идут и почему порядок (очередь значима) и темы (порядка нет)
BACKLOG.md что можно взять — только задачи секции проекта (по умолчанию ядро/инфра)
SPRINT.md какая цель и какой набор под неё одна: «Набор»
REJECTED.md что ушло без реализации и почему

Шапку SPRINT.md пишет sprint startтем же мета-блоком, что у задачи: поле на строку, - **Цель:** [Заголовок](items/slug.md), - **Начат:** датой, - **Спринт:** слагом, которым метится урожай. Прежняя форма (три поля одной строкой через ·) читается по-прежнему и уходит сама: файл переписывается на следующем sprint start и очищается на sprint close.

Секции — единственные заголовки ## в индексе: любой другой ## в преамбуле проверка сочтёт секцией. Внутри секции беклога порядок значения не имеет — порядка в беклоге нет вовсе.

Секции «блокеры» среди них нет. Блокер — состояние, а не полка: он живёт до ответа человека, а следы остаются вопросами в файлах задач распущенного спринта. Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила бы правилу «эскалируем немедленно», поэтому init её не заводит, а check говорит о ней в чужом беклоге. Переезжаешь с такой секцией — удали её. В секции «порядок» очередь значима и обосновывается прозой; двигают строку move <slug> --section порядок --after <другой>.

Индексы производны: расходятся с файлом — правим индексы (check --fix). Строку руками не пишут.

Отсюда же ответ на «а если оборвётся посередине». Мутация сперва проверяет всё и складывает правки, и только потом пишет: сначала все временные файлы, потом переименования подряд. Полной транзакции на несколько файлов файловая система не даёт, но окно сжато до цепочки переименований, а всё, что в нём может разъехаться, — производное: файлы целы, индексы восстанавливает check --fix. Поэтому отказ на второй задаче из пяти не оставляет первую переписанной при нетронутых индексах.

SPRINT.md и есть артефакт заморозки: без него набор существует только в контексте сессии, и нарушение заморозки ненаблюдаемо.

REJECTED.md

Туда уходит задача, покинувшая беклог без реализации. Строку пишет tasks.py close --reason, а check следит за форматом:

- 2026-07-23 `versii-kachestvo-repaki` — Версии и качество одного тайтла.
  Причина: калибровка болей — не боль, ни разу не возникло за полгода.
  Была секция: инфра.

Реализованные сюда не попадают: у них остаётся коммит и документация. У выкинутой не остаётся ничего — и через квартал она возвращается тем же текстом. Это первое место, куда смотрит дедупликация при заведении.

Запись не запрещает завести задачу заново: изменился контекст — заводим и ссылаемся на строку, объясняя, что изменилось.

Теги

Единственный механизм разметки, потому что list --tag уже умеет отбирать по ним порцию разбора. Отдельных полей меты под это не заводим.

  • goal:<слаг> — цель, которой служит задача. Обязателен: задача без цели не попадёт ни в один спринт.
  • kind:<род> — род работы: feature | fix | chore | research. Словарь закрыт, значение ровно одно. Обязателен у задачи (без него sprint take откажет), у цели запрещён, у идеи и эпика необязателен. Ставится add --kind / edit --kind; --kind заменяет прежнее значение, а не добавляет второе. Смысл рода и почему он тегом, а не префиксом — в SKILL.md, раздел «Род работы».
  • question — в файле есть неразобранный раздел «Вопросы».
  • sprint:<слаг> — задача заведена в этом спринте; по нему отбирается первая порция разбора («урожай спринта»). Ставится сам: слаг спринта заводит sprint start (по умолчанию — дата начала, он же пишется в SPRINT.md), и add при открытом спринте помечает заводимое. Тег, который надо помнить ставить руками, не ставится никогда — а на нём висит правило «первая порция разбора — урожай прошедшего спринта».
  • decomposed — на цели: разложена на задачи (см. «Файл цели»).

Отбор — list --tag a,b: перечисленные через запятую теги требуются все сразу (это И, не ИЛИ). Тег, которого нет ни у одной задачи, list называет вслух: молчаливый ноль читается как «таких задач нет», а чаще это опечатка.

Свои теги проект заводит свободно (партия ревью review-ГГГГ-ММ-ДД, тема, источник) — словарь не фиксирован. В индексы теги не выносим: индексы производны, отбор делает list --tag, а не глаза.

Тест «готова к взятию»

Задача готова, если из файла отвечаются четыре вопроса:

  1. Что станет наблюдаемо иначе, когда она сделана — снаружи: пользователю, владельцу сервиса или разработчику. «Отрефакторить X» — не ответ; «перестанет ломаться Y при Z» — ответ. У kind:chore адресат — разработчик, и это законно: «уедет последний вызов устаревшего API» — ответ, а не отговорка. Род объявлен как раз затем, чтобы такие задачи не выдумывали себе пользовательскую пользу.
  2. Каких границ это касается — раздел «Затрагивает». Без него задачу нельзя оценить: остаётся судить по длине текста.
  3. По чему видно, что закончено — критерии приёмки с оракулами.
  4. Какой цели она служит — тег goal: и одна строка «почему именно этой».

Не отвечается первый, второй или третий вопрос → это идея ([idea]), её место в штурме. Не отвечается четвёртый → либо цель есть и не проставлена, либо задача не служит ничему — тогда её не надо заводить.

Отвечается всё, но задача не делается одним заходом и не мерджится целиком → эпик ([epic]), сперва декомпозиция. Эпик временен и исчезает после разбора; цель ([goal]) постоянна — не путать.

Тест применяется при заведении и при переоценке. К старым задачам, которых операция не касается, задним числом не применяется — беклог не переоформляют «заодно».