--- name: tasks description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). Заведение задачи, идеи или цели из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, мозговой штурм идеи, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Ритуал между спринтами — скилл session. Не реализует задачи — этим занимается пайплайн проекта. --- # Задачи Задачи — каталог markdown-файлов. Одна запись = один файл `items/.md` плюс строка **ровно в одном** индексе. Скилл владеет **форматом и содержимым**: заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует идеи. Чем он **не** владеет: ритуалом между спринтами (разбор вопросов → разбор прошедшего спринта → переоценка → выбор цели и набор) — это скилл `session`; и выполнением задачи — это пайплайн проекта. ## Четыре правила, из которых всё следует Ситуация не покрыта инструкцией — решай по ним. 1. **Беклог гниёт с той стороны, где его пополняют.** Заведение — самая частая операция и с худшим отказом: из одного разговора рождается пять файлов, а переоценка потом разгребает то, чего не надо было заводить. Дедупликация и фильтр на входе дешевле любой чистки. Заводим только то, что **не делаем сейчас** и о потере чего пожалеем. 2. **Файл — источник истины, индексы производны.** Разошлись — неправы индексы. Согласованность механизируема и проверяется командой, а не вниманием: всё, что ловит `tasks.py check`, не должно попадать ни в чек-лист, ни в промпт. Поэтому **хук живёт в мета-строке файла**, а строка индекса его лишь повторяет: пока хук лежал только в индексе, восстановление пропавшей строки теряло его молча и навсегда. Единственное исключение намеренное: **в каком индексе лежит задача, знают индексы** — «в спринте» это свойство спринта, а не файла, поля-состояния нет. 3. **Причина переживает запись.** Выкинутая без причины задача вернётся через квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не оставляет ничего, поэтому у неё есть `REJECTED.md`. 4. **Порядка нет, есть цель.** Ни в секциях, ни списком: «что делать дальше» отвечает набор спринта, а между спринтами порядок не нужен никому — брать задачи вне спринта запрещает заморозка. Поэтому нет ни приоритетов, ни «повысить», ни «встать раньше»: вместо повышения — смена цели или включение в набор. ## Раскладка Каталог задач — **`docs/tasks`, жёстко**: это часть [канона документов](../canon/references/canon.md), и подгоняется под него проект, а не наоборот. ``` docs/tasks/ items/ задачи и цели файлами, .md, слаги английские PLAN.md оглавление целей: линия (упорядоченная) и кусты BACKLOG.md что можно взять — только задачи, целей здесь нет SPRINT.md текущий спринт: цель, набор, дата REJECTED.md ушедшее БЕЗ реализации, с причиной и датой ``` Правило, снимающее путаницу: **`BACKLOG.md` — то, что берут; `PLAN.md` — то, подо что берут.** Цель в спринт взять нельзя, поэтому в списке берущихся ей не место. **Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (спринт не может продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и записи в такой секции не успевают жить. Следы блокера остаются вопросами в файлах задач распущенного спринта. Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила бы правилу «блокер эскалируется немедленно», поэтому `init` её заводить отказывается, а `check` о ней говорит. **Проекту, который переезжает с такой секцией, её надо удалить** — это единственное место, где это сказано. **Задача живёт в одном индексе за раз.** Взята в спринт — строка переезжает из `BACKLOG.md` в `SPRINT.md`; вышла — обратно. Файл в `items/` при этом **не двигается**: он и есть запись, индексы лишь показывают, где она числится. **У сделанной задачи записи не остаётся** — файл и строка удаляются (`close --implemented`). Ей хватает коммита и документации проекта; вторая запись была бы вторым домом для того же факта. Вопрос «что было в спринте N» отвечается даром: `SPRINT.md` лежит под git, `git log -p docs/tasks/SPRINT.md` отдаёт историю всех наборов без отдельного журнала. ## Цели **Цель — такой же файл в `items/`, тип `[goal]`**, перечисленный в `PLAN.md`: либо звено упорядоченной **линии** продукта (с обоснованием порядка прозой), либо тематический **куст** — цель, в последовательность не встающая («прочность слияния», «журнал и пересборка»). Без второй части половина целей была бы нигде не перечислена: находки ревью не служат ничему из линии. - **Список задач цели выводится, а не хранится.** В теле цели — зачем она и что считается её завершением; перечня задач там нет. Он был бы третьим индексом и поехал бы на первой же закрытой задаче, а `check` про него не знает. Связь однонаправленна: задача несёт тег `goal:<слаг>`, перечень даёт `tasks.py list --goal <слаг>`. - **Статус цели выводится.** Цель закрыта, когда у неё не осталось открытых задач; `[x]`/`[~]` руками не ведутся, а `close` цели с живыми задачами скрипт запретит. Единственная оговорка: цель без задач неотличима — «ещё не разобрана» или «всё закрыто». Различает **тег `decomposed`** в мета-строке цели: он ставится, когда цель разложена на задачи. Тег, а не строка в теле — потому что проверяется механически: `check` **напоминает** о нём у пустой цели (замечанием, не ошибкой — неразобранная цель это законное состояние), а `check --fix` сам проставляет его цели, у которой задачи есть. - **`[goal]` и `[epic]` — разные вещи.** Цель **постоянна**: живёт, пока живёт направление. Эпик **временен**: это задача, которая не мерджится целиком, её разбирают, и он исчезает. Два срока жизни под одним словом разъезжаются, поэтому слова два. ## Инструмент (`tasks.py`) Пусть `tk="$CLAUDE_PLUGIN_ROOT/skills/tasks/scripts/tasks.py"`, а `D` — `docs/tasks` от корня проекта. `--dir` стоит в примерах намеренно: вызов из подкаталога — обычное дело. ``` python3 $tk check --dir D # согласованность индексов + здоровье python3 $tk check --dir D --fix # + починить дрейф (секция, заголовок, дубли, хук, дом) python3 $tk list --dir D [--stale] [--section S] [--type T] [--tag a,b] [--goal S] [--index …] [--questions] python3 $tk add --dir D --slug S --title T [--type goal|idea|epic] [--section S] [--goal G] [--hook H] [--tag a,b] python3 $tk edit S --dir D [--title T] [--hook H] [--type T] [--goal G] [--add-tag a,b] [--rm-tag c] python3 $tk move S --dir D --section S [--reason R] [--after S | --first] python3 $tk close S --dir D --reason R # в REJECTED.md + удалить (ушла без реализации) python3 $tk close S --dir D --implemented # просто удалить (реализована и закоммичена) python3 $tk reopen S --dir D --reason R # вернуть закрытую: приёмка не сошлась python3 $tk sprint start --goal S --dir D | take S… | drop S… --reason R | close [--dissolve --reason R] python3 $tk init --dir D [--sections …] [--plan-sections …] [--items …] [--backlog …] … python3 $tk adopt scan --from … | apply --plan … # разовая адаптация, references/adopt.md ``` **Коды выхода — единый словарь; на нём ветвятся скиллы, а не на тексте вывода:** | Код | Что случилось | Что делать | | --- | --- | --- | | 0 | сошлось / сделано | дальше по сценарию | | 1 | **только `check`:** найден дрейф индексов и файлов | `check --fix`, остаток разобрать | | 2 | ошибка употребления: аргументы или нарушенное правило | читать сообщение, это отказ по существу | | 3 | окружение: каталог не найден, конфиг битый или мимо диска | чинится путём или `docs/.pm.json`, повтор не поможет | | 4 | внутренний сбой | дефект скрипта, доложить | Различать 1 и 3 обязательно: «дрейф в беклоге» — рабочая ситуация, «каталога нет» — нерабочая, и одинаковая реакция на них была бы неверна в обоих случаях. Тип — английское ключевое слово `goal` / `idea` / `epic` / `task` (как и прочие токены команд); `task` префикса не несёт, остальные кодируются `[goal]`/ `[idea]`/`[epic]` в заголовке. Текст задачи при этом русский. **Мутации правят файл и индексы заодно** — руками строку индекса или мета-строку не пиши, зови `add`/`edit`/`move`/`close`/`sprint`. Смена заголовка, хука, типа, цели и **тегов** — это `edit`: он держит H1, мета-строку и индекс в синхроне. Снятие тега — `--rm-tag` (после ответа на вопрос снимается `question`), смена цели — `--goal`, он заменяет прежний `goal:*`. **Переезд между индексами — следствие смены типа, а не отдельная команда.** `edit --type goal --section <часть плана>` переносит строку из `BACKLOG.md` в `PLAN.md` (и обратно `--type task --section <секция беклога>`); `move` двигает только внутри одного индекса и пишет причину. `--section` у `edit` работает **только** при таком переезде — иначе он отсылает к `move`, потому что смена секции без причины и есть тот дрейф, который потом никто не объяснит. Задача в наборе спринта тип не меняет вовсе: сперва `sprint drop`. Тело задачи скрипт не трогает: `add` кладёт заголовок, мета-строку и шаблон с подсказками, тело дописываешь редактором (пока плейсхолдер на месте, `check` напоминает). `check` — единственный судья согласованности; что именно он ловит, скажет его вывод, здесь не пересказываем. Гоняй его **в начале сессии** и **после каждой правки**, даже если правил мутациями: дрейф мог накопиться раньше. Накопившееся чини `check --fix` — он детерминированно правит то, где истина однозначна (секция, заголовок, дубли, хук из индекса в файл, строка в чужом индексе, пометка `decomposed` у цели с задачами), а неоднозначное (задача сразу в двух индексах, нечего восстанавливать) печатает отдельной пометкой `НЕОДНОЗНАЧНО` — это тебе, и это идёт строкой доклада. **Ссылка на исчезнувший файл в пометку не попадает:** `--fix` её просто не трогает, и она остаётся `ОШИБКА` обычного `check` — то есть видна, но в докладе её надо назвать отдельно. `--fix` правит **и файлы** — ровно в двух местах, где источник ровно один и выбирать не из чего: хук, оставшийся только в индексе, переезжает в мета-строку, и цель, у которой есть задачи, получает тег `decomposed`. Оба случая печатаются поимённо. **Что механизировано, а что нет.** Критерии приёмки проверяются у задачи, взятой в набор (`sprint take` и `check` по задачам спринта): число пунктов — жёстко (меньше двух — отказ, больше пяти — замечание), наличие оракула — **эвристикой** по слову «оракул» в пункте. Настоящий оракул от слова «оракул» машина не отличает, поэтому эвристика даёт только замечание, и в докладе это называется как есть: «проверено число пунктов, годность оракулов — глазами». Формат файла, мета-строки, слага, индексов и `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). ### Прийти в репозиторий, где задачи уже как-то ведутся Разовая операция: вывести каталог задач из старой раскладки беклога, `TODO.md`, заметок или списка шагов в плане — [references/adopt.md](references/adopt.md). Сюда же относится переименование транслитных слагов в английские: оно делается **одним проходом вместе с починкой перекрёстных ссылок**, а не по одному слагу. Если переводить надо не только задачи, а весь `docs/` — это скилл `av-dev-pm:canon`, и он зовёт этот сценарий сам на своём шаге. ### Декомпозиция и штурм идеи [references/split.md](references/split.md). Обе операции превращают одну запись в несколько, и у обеих есть проверяемый тест: части должны **мерджиться порознь** и **каждая давать видимую пользу**, а у штурма исход «выкинуть» — полноправный. ### Гигиена полей Правится по ходу любой операции, которая задачи касается (но не «заодно» по всему беклогу): - **протухший хук** — задача изменилась, а хук отвечает на старый вопрос; особенно после ответа на вопрос задачи: «Решено: …» на «почему это лежит в беклоге» уже не отвечает. Переписывается `edit --hook …` — он правит мета-строку файла и строку индекса заодно; - **вопрос, застрявший в прозе** — вынимается в раздел «Вопросы» плюс тег `question` (`edit --add-tag question`), иначе он не виден ни `list --questions`, ни правилу «задача с открытым вопросом в набор не берётся»; - **тег, который некому снять** — `question` после ответа снимается `edit --rm-tag question` вместе с записью ответа в тело **и опустошением раздела «Вопросы»**: судит раздел, а не тег (`references/task-format.md`); - **свойство репозитория в рамках** — номер миграции, хеш, версия зависимости: в лежалой задаче протухает молча и становится ложной рамкой. Снимается; снимок берётся при постановке, а не при заведении; - **предписание процесса в теле** — «делать таким-то профилем ревью», «взять такой-то агент»: это второй дом для правила выбора и путь понизить требования решением, принятым до проектирования. Снимается. ## Переносимость Скилл независим от **языка программирования, сборки, CI и трекера**: он ничего не знает ни про Go, ни про npm, ни про конкретный багтрекер — задачи для него просто каталог markdown. Текст задач — русский (язык документации проекта); зашита только латиница слага. OpenSpec ему тоже не нужен. - **Каталог задач — `docs/tasks`, жёстко**, и `--dir` передаётся явно всегда: раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект действительно новый, а перевод чужой раскладки делает `av-dev-pm:canon`. У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и полагаться на него скилл не должен: молча найденный чужой каталог это дрейф. - **Настройки живут в `docs/.pm.json`**, ключ `tasks`: **имена** файлов и заголовков, и только если они отличаются от умолчания. Один конфиг на весь канон, а не по одному на каталог. Неизвестный ключ — код 3 на любой команде, так что лишнее слово в этом объекте останавливает работу с задачами целиком. - **Секции беклога** берутся из заголовков `##` индекса как есть; их количество и названия — дело проекта (умолчание `ядро` / `инфра`). **В конфиге их нет** — второй список разошёлся бы с заголовками молча. ### Вызов из другого плагина `$CLAUDE_PLUGIN_ROOT` раскрывается **только внутри своего плагина**: пайплайн задачи, конвейер ревью и любой другой чужой контекст до `tasks.py` по этой переменной не дотянутся. Мост — **вызов скилла через пространство имён**, а не путь: > Чужой контекст зовёт `Skill av-dev-pm:tasks` и называет, что нужно сделать > («закрой задачу `<слаг>`, реализована»). Скилл разрешает свой > `$CLAUDE_PLUGIN_ROOT` сам. Путь наружу не выносится вовсе. Плагина в проекте нет — вызов не разрешится, и вызывающий **не выдумывает путь и не правит индекс руками**, а сообщает в докладе, что учёт задач остаётся за владельцем. ## Слоты проекта Почти всё, что скиллу нужно знать о проекте, отвечает канон структурой: куда переезжает суть реализованной задачи — `openspec/specs`, `adr/`, архив change; какие в проекте оракулы — семантика гейта в `CLAUDE.md`. Отдельными слотами остаётся то, чего из раскладки не вывести. **Проект дописывает в `CLAUDE.md`**: 1. **Что такое «сделана»** — чем задача выполняется (пайплайн проекта) и что входит в его определение готовности. Скилл требует лишь **форму**: пайплайн проекта пройден + критерии приёмки проверены поимённо. 2. **Что считается необратимым** и потому спрашивается у человека всегда (деплой, выкладка наружу, удаление или перезапись данных). Ни того ни другого скилл не угадывает: не нашёл — спрашивает пользователя, а не подставляет умолчание. ## Общее для всех сценариев - **Развилки — пользователю.** Через `AskUserQuestion`, с уже сформулированным предварительным суждением (**рекомендация — первым вариантом**). Что выкинуть, под какую цель отнести, какая рамка идеи верна — решение пользователя. Слаг, формулировка, порядок строк в индексе — механика, делаем сами. - **Не больше трёх вопросов за раз.** Пачка длиннее трёх тяжела для ответа; решений больше — веди **несколько итераций** диалога по ≤3, а не один перегруженный запрос. Между итерациями применяй уже решённое. - **Границы покрытия в отчёте.** Любая сессия разбора, штурма или интейка заканчивается строкой «просмотрено N из M, не трогали — …». Отчёт без неё сообщает «беклог разобран», не сообщая, какая его часть осталась нетронутой. - **Ничего не удаляем молча.** Файл исчезает только через `close` — `--reason` (ушла без реализации) или `--implemented` (реализована). Прямого `rm` нет. - **Слаги английские**, kebab-case, не транслит: `tie-break-equal-completeness`, а не `taj-brejk-pri-ravnoj-polnote`. Заголовки, тела и хуки — русские. ## Чего этот скилл не делает Не пишет код, не заводит спеки и предложения об изменении, не берёт задачу в работу — этим занимается пайплайн проекта. Не ведёт спринт и не проводит сессию между спринтами — это `session`. Не решает за пользователя, что важно. Не переоформляет существующие задачи «заодно»: правится то, чего касается операция.