--- name: task-track description: Ведение задач как каталога markdown-файлов (одна задача = один файл в items/ + строка в BACKLOG.md). У каждой задачи есть тип (feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. У проекта есть стадия (build — беклог это план стройки, порядок строк значит зависимость; support — очередь правок, порядок значит важность). Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей, смена стадии и проверка согласованности индекса. Использовать, когда просят добавить задачу или идею, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат, объявить стадию или проверить беклог. Каталог отстал от версии раскладки — это скажет tasks.py check, а повышает проект скилл av-dev:canon по общему журналу версий. Расстановка приоритетов и разбор накопившегося — скилл av-dev:task-groom. Не реализует задачи — этим занимается скилл решения задачи. --- # Задачи Задачи — каталог markdown-файлов. Одна задача = один файл `items/.md` плюс строка в `BACKLOG.md`. Скилл владеет **форматом и содержимым**: заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё. Чем он **не** владеет: **очередью** — что делать следующим и что перестало быть важным, решает скилл `task-groom`, а этот скилл лишь даёт ему операции; и выполнением задачи — это конвейер проекта. ## Шесть правил, из которых всё следует Ситуация не покрыта инструкцией — решай по ним. 0. **Стадия решает, что значит порядок строк.** Проект живёт в одной из двух стадий, и обе ведут один и тот же беклог, но читают его по-разному. На **стройке** (`build`) беклог это план от базы к деталям: порядок — зависимость, «раньше нельзя». На **доработке** (`support`) беклог это очередь правок: порядок — важность, «раньше лучше». Из этого следует остальное — сколько у беклога секций, как его пополняют, что значит его опустошение и нужен ли груминг. Стадия объявлена ключом `[tasks] stage`; молчание ответом не считается, и `check` без неё отказывает. 1. **Беклог гниёт с той стороны, где его пополняют — на доработке.** Заведение там самая частая операция и с худшим отказом: из одного разговора рождается пять файлов, а переоценка потом разгребает то, чего не надо было заводить. Дедупликация и фильтр на входе дешевле любой чистки: заводим только то, что **не делаем сейчас** и о потере чего пожалеем. **На стройке правило не применяется**, и это не послабление. Список стройки пишется вперёд целиком — он и есть замысел, — а фильтр «не заводи то, чего не делаешь сейчас» запретил бы написать план дальше первого шага. Дедуп остаётся в обеих стадиях: две записи об одном плохи всегда. 2. **Файл — источник истины, индекс производен.** Разошлись — неправ индекс. Согласованность механизируема и проверяется командой, а не вниманием: всё, что ловит `tasks.py check`, не должно попадать ни в чек-лист, ни в промпт. Поэтому **«зачем» живёт в мете файла**, а строка индекса его лишь повторяет: пока поле лежало только в индексе, восстановление пропавшей строки теряло его молча и навсегда. Единственное исключение намеренное: **порядок строк в беклоге** — он свойство списка, а не задачи, и в файле ему места нет (правило 4). 3. **Причина переживает запись.** Выкинутая без причины задача вернётся через квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не оставляет ничего, поэтому у неё есть `REJECTED.md`. 4. **Порядок строк — единственное, чего в файле нет.** Он значим в обеих стадиях, и назначает его человек: на стройке — раскладывая шаги по зависимости, на доработке — на груминге. Машина порядок не выводит и не угадывает; всё, что она делает сама, — ставит машинную позицию в **конец** секции и говорит об этом вслух. **Дом порядка — индекс, а не файл.** Положи его в файл числом, и два соседних файла смогли бы утверждать одно и то же место, а строка индекса — противоречить обоим. Одно место в очереди назначено **не человеком, а типом**: **сырьё** (`research` без раздела «Вопрос») стоит в конце своей секции. Его не берут, и между берущимся оно каждый раз требует открыть файл, чтобы это понять. Раз это выводится, проверяет и чинит это машина. 5. **Тип решает, что с задачей можно делать.** Тип — вторая ось и первое поле меты: от него зависит, какие разделы обязательны в теле и берётся ли запись в работу. Словарь закрыт; ни один тип не подошёл — значит, в записи их два, и её надо разделить. ## Раскладка Каталог задач — **`tasks/` в корне репозитория, жёстко.** Он принадлежит этому скиллу, а не канону документов: учёт работ ведут и в проекте, который к канону не приведён, и каталога `docs/` там нет вовсе. Внутри `docs/` задачи лежали до версии канона 11; непереехавший проект скрипт по-прежнему находит, но новый заводит только в корне. ``` tasks/ items/ задачи файлами, .md, слаги английские BACKLOG.md что можно взять. Порядок строк в секции значим, и значит он разное на разных стадиях REJECTED.md ушедшее БЕЗ реализации, с причиной и датой ``` **Индекс один.** `REJECTED.md` индексом не считается: он не говорит, где запись числится, — это кладбище ушедшего. **Секции беклога называет проект**, и `check` проверяет у них ровно две вещи: что секция есть хоть одна и что на стройке она **одна**. Смысла секции не несут — это полки домена (`Ядро`, `Инфра`), — и подгонять их имена под свой вкус скрипт права не имеет. Поле меты, называющее полку, зовётся **Категория**. **Заголовок секции отбит пустой строкой с обеих сторон и написан с прописной**; отбивку правит `check --fix`. Написание секции в мете файлов он тоже правит: имя секции принадлежит заголовку индекса, файл на неё только ссылается. **Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (работа не может продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и записи в такой секции не успевают жить. Следы блокера остаются вопросами в файлах задач. Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила бы правилу «блокер эскалируется немедленно», поэтому `init` её заводить отказывается, а `check` о ней говорит. **Проекту, который переезжает с такой секцией, её надо удалить** — это единственное место, где это сказано. **Порядок строк — единственное, чего в файле нет** (правило 4). Отсюда следствие для всякой машинной правки индекса: восстановленная или перенесённая строка встаёт **в конец своей секции**, и скрипт об этом говорит. Молчаливая вставка выдала бы машинную позицию за решение человека — а решение это его. **У сделанной задачи записи не остаётся** — файл и строка удаляются (`close --implemented`). Ей хватает коммита и документации проекта; вторая запись была бы вторым домом для того же факта. Вопрос «что было сделано и когда» отвечается даром: индекс лежит под git, а закрытие коммитится отдельным коммитом учёта — `git log -p tasks/BACKLOG.md` отдаёт историю без отдельного журнала. Куда запись может переехать и какой командой — весь набор переходов: ```mermaid stateDiagram-v2 state "BACKLOG.md — что берут" as B state "REJECTED.md — ушла без реализации" as R state "записи нет — реализована" as D [*] --> B: add --type feature|fix|chore|research B --> B: move --after | --first | --section B --> D: close --implemented B --> R: close --reason D --> B: reopen --reason R --> B: reopen --reason ``` Состояния здесь — **где числится строка**, а не где лежит файл: файл `items/.md` не двигается ни на одном переходе. Стрелок «руками» на схеме нет намеренно — каждый переход это команда, и другого способа его совершить не существует. Схема — **сводка**: условия и оговорки живут в тексте разделов, и при расхождении прав текст. ## Две стадии **Стадия проекта — ось, и решает она, что значит порядок строк беклога.** Значения два, дом — ключ `[tasks] stage` в `.av-dev.toml`. | | `build` — стройка | `support` — доработка | | --- | --- | --- | | Порядок строк | зависимость: раньше **нельзя** | важность: раньше **лучше** | | Секции | ровно одна: список от базы к деталям | полки домена, сколько нужно | | Заведение | список пишется вперёд целиком | по одной, по мере появления | | Пустой беклог | план исчерпан, стройка окончена | нормальное состояние | | Груминг | не применяется; замысел сменился — план пересматривается целиком | основная гигиена, порциями по 5–8 | | Залежалость | не считается: шаг ждёт своей очереди законно | считается, `list --stale` | **Стадия называется явно, и молчание ответом не считается.** Без неё порядок строк нечем прочитать: переставить строку значит на стройке сломать план, а на доработке — принять решение о важности, и это разные действия. `init --stage` обязателен, `check` без ключа отказывает, `check --fix` его не подставляет: какая стадия у проекта, знает человек, а подставленное умолчание соврало бы ровно там, где по нему принимают решение. **Секций на стройке одна, и это не педантизм.** Порядок там — зависимость, и разложенный по полкам список перестаёт быть планом: два шага из разных секций уже не сравнить. На доработке полки законны — правки независимы, и очередь внутри полки самостоятельна. **Переход — событие, а не настройка.** `tasks.py stage support` переносит остаток беклога в первую новую секцию, правит «Категорию» в файлах и говорит, что порядок с этого момента значит другое. Датой ему служит коммит: отдельного журнала ради одной строки не заводится. Признак созревания наблюдаемый — беклог стройки исчерпан, и `check` об этом напоминает; **запретить переход раньше скрипт не берётся**: «приложение построено» решает человек, а не счётчик строк. Обратный переход (`stage build`) разрешён и устроен так же. Он редок — проект уходит на стройку заново разве что при переделке замысла целиком, — но запрещать его было бы запретом на то, что иногда и правда случается. ## Чего у задач больше нет **Тип `goal` и `ROADMAP.md` упразднены.** Цель была зонтиком над параллельными направлениями: она нужна там, где список работ нельзя выстроить в один порядок, и очередь идёт поперёк направлений. У проекта, который ведёт один человек, такого не бывает — на стройке список линеен по зависимости, на доработке правки независимы, — и зонтик не стоял ни над чем. Роадмап при этом отвечал на свой вопрос наполовину: «чего ещё не умеет» — это «что осталось в беклоге», то есть пересказ второго индекса. Вторая половина, «что уже умеет», живёт в двух домах и без него: нормативное поведение — в `openspec/specs/`, а когда и в каком порядке оно появилось — в `git log` индекса и коммитах задач. Вместе с целью ушли: секция `Готово` (её ответ дают спеки и история), теги `goal:<слаг>` и `decomposed`, поле меты `Секция` (осталась `Категория`), раздел `Завершение`, ключи `[tasks] roadmap` и `[tasks] completion_heading`, флаги `add --goal`, `list --goal`, `list --index`, `edit --goal`, `edit --section` и `init --roadmap`. Встретились в проекте — `check` назовёт их поимённо, `check --fix` снимет теги, а запись типа `goal` оставит человеку: во что она превращается — в задачу или в ничто, — машина не решает. **Тип `[epic]` упразднён раньше и не вернулся.** Слишком крупный шаг дробится на шаги помельче, стоящие в списке подряд. ## Тип записи **Тип решает, что с задачей можно делать.** Вторая ось скилла — первая стадия. Перечень осей всего процесса и того, чего каждая **не** решает, — [shared/axes.md](../../shared/axes.md). Дом типа — **поле меты `Тип` первой строкой**; эмодзи в заголовке H1 от него производна, её ставит `add` и чинит `check --fix`. | Тип | Обязательные разделы | Устав | | --- | --- | --- | | ✨ `feature` | `Затрагивает`, `Критерии приёмки` | [task-feature.md](references/task-feature.md) | | 🐞 `fix` | `Воспроизведение`, `Затрагивает`, `Критерии приёмки` | [task-fix.md](references/task-fix.md) | | 🧹 `chore` | `Затрагивает`, `Критерии приёмки` | [task-chore.md](references/task-chore.md) | | 🔬 `research` | `Вопрос`, `Куда ляжет ответ` | [task-research.md](references/task-research.md) | Берутся в работу все четыре: записи, которую нельзя взять, больше не существует. Сверх обязательных у любой задачи допустимы `Рамки` и `Вопросы`. Раздел не из схемы своего типа — **замечание, а не ошибка**: свой раздел законная вольность проекта, но `Воспроизведение` у `chore` почти всегда значит, что тип проставлен не тот, и сказать об этом стоит, не запрещая. **Прежних осей было две, и ортогональность у них была фальшивой.** Тип записи (`goal`/`idea`/`task`) и род работы (`kind:<род>` тегом) давали двенадцать клеток произведения, из которых законны были шесть: у цели род запрещён, у задачи обязателен, у идеи пуст. Плюс алгоритм работы крепится не к `task`, а к `fix` и `research` — то есть к роду. Оси схлопнуты, тег `kind:` упразднён. **Тип `idea` упразднён вместе с ними.** Он значил не род работы, а **состояние незаполненности** — «первый, второй или третий вопрос теста готовности не отвечается», — а состояние типом быть не может: оно меняется по мере того, как запись дописывают, а тип меняют командой. Теперь это состояние называется честно: `research` без раздела «Вопрос» — **сырьё**. В работу не берётся ровно как прежняя идея, лежит в конце своей категории и отбирается `list --raw`. Словарь **закрыт**. Открытый разъедется на синонимах — `bug`, `bugfix`, `fix`, `defect`, — и отбор по типу перестанет отвечать на свой единственный вопрос. Ни один тип не подходит — это сигнал, что в задаче их два и её надо разделить. **Требуется тип там, где по нему принимают решение:** `ready` без типа откажет, потому что не знает, каких разделов требовать. `check` о пропаже только **напоминает** — беклог, заведённый до появления типа, законен, и переоформлять его «заодно» здесь не просят. **Тип не выбирает метку ревью и глубину проверки.** Профиль выбирается по факту изменения, а не по типу задачи: `chore` бывает миграцией схемы, `fix` — правкой публичного контракта. Правило «предписание процесса в теле задачи снимается» типом не отменяется, а подтверждается: он описывает работу, а не то, как её проверять. **Стадия проекта их тоже не выбирает**: изменение на стройке ничем не проще того же изменения на доработке, и метку ему по-прежнему назначает разметка. **Одно исполнителю тип всё же говорит — каким сценарием работу вести, и то не один.** Скилл `av-dev:code-resolve` выбирает сценарий связкой из двух признаков: тип **предлагает** (`chore` — обслуживание, `research` — разведка), а подтверждает его предмет работы — есть ли что менять в спеках. Признаки разошлись — работа останавливается, и тип меняется здесь, командой `edit --type`, а не переклеивается исполнителем по ходу. Метку и глубину это по-прежнему не задаёт: их называет разметка изменения, а на прогоне без change — сам сценарий. ## Как написана задача Три требования к тексту. Первое — про заголовок, два остальных про то, чтобы задачу можно было **оценить, не открывая код**. **Заголовок отвечает на вопрос своего типа.** Вопросов два, поэтому и форм две: | Тип | Отвечает на | Пример | | --- | --- | --- | | ✨ `feature`, 🐞 `fix`, 🧹 `chore` | что нужно сделать | Печатать поле одним куском кода | | 🔬 `research` | о чём разведка | Подсказка следующего хода | Задача — **глаголом в неопределённой форме**, перед ним допускается «не»: «Не отбрасывать молча лишние символы в ходе», а не «Лишние символы молча отбрасываются». Описательный заголовок называет **состояние**, а из состояния не видно, чего от работы ждут: «Ничья объявляется, пока клетки есть» одинаково читается и как жалоба, и как задание, — и в списке, где решают «брать или не брать», это разные вещи. `research` формы действия не несёт **намеренно**: её исход знание, а что делать — ещё неизвестно, и заголовок-действие обещал бы решённость, которой нет. `check` считает заголовки не в форме действия и печатает **число** в блоке здоровья, не замечанием на файл: проверка эвристическая (первое слово на `-ть`/`-ти`/`-чь`), а беклог, заведённый до правила, не переоформляют «заодно». Годность формулировки — не машине: её смотрит [агент вычитки](#вычитка-два-прохода-а-не-один). **Функции и границы, а не намерения.** Задача называет, что система начнёт делать, и какие границы это трогает: эндпоинт или команду, таблицу и миграцию, формат на диске, публичный тип пакета, внешний сервис. Перечень живёт разделом «Затрагивает» (форма — [references/task-format.md](references/task-format.md)) и требуется к взятию в работу. Без него задача оценивается по объёму текста, а не по объёму поверхности, — и оценка систематически занижена ровно там, где текст короткий, а границ много. Названы **границы**, а не то, как они изменятся: план реализации живёт в предложении об изменении, а не в задаче. **Предметно, но без усложнения.** Текст задачи читает человек, который решает, брать её или нет, и делает это по строке индекса и одному экрану тела. Язык — общий для всех проектных текстов, и дом у него один: [shared/language.md](../../shared/language.md) — информационный стиль, применённый к задачам и документам канона; там же таблицы англицизмов и жаргона и то, что из стиля отброшено намеренно. Задаче он даёт четыре требования, которые нарушаются чаще прочих: - **глагол вместо отглагольного существительного**: «обработчик не проверяет владельца», а не «проверка владельца не осуществляется»; - **факт вместо оценки**: «время ответа доходит до 800 мс», а не «работает медленно». Оценка без факта рядом — настроение, а не сведение; - **англицизм с живым русским аналогом заменяется**: не «зафиксить флоу», а «починить порядок доставки». Имя вещи не переводится: слаг, команда, тип в коде, `API`; - **термин не из документов проекта вводится одной строкой** или не употребляется. Свой словарь у задачи — самый дешёвый способ сделать беклог нечитаемым для того, кто вернётся к нему через квартал. И одно требование, которое есть только у задачи: **сложность формулировки — не признак сложности работы.** Задачу, которую не удаётся сказать просто, чаще всего не удаётся и оценить: это либо две задачи, либо сырьё. Эти правила — про **язык**, а не про объём: короткая задача без границ хуже длинной с ними. ## Инструмент (`tasks.py`) Пусть `tk="$CLAUDE_PLUGIN_ROOT/skills/task-track/scripts/tasks.py"`, а `D` — `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] [--raw] [--questions] python3 $tk add --dir D --slug S --title T --type feature|fix|chore|research [--section S] [--why «зачем»] [--tag a,b] python3 $tk edit S --dir D [--title T] [--why «зачем»] [--type T] [--add-tag a,b] [--rm-tag c] python3 $tk move S --dir D [--section S] [--reason R] [--after S | --first] # без --section — текущая секция 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 ready S… --dir D # схема типа выполнена — можно брать в работу python3 $tk stage --dir D # показать стадию python3 $tk stage support --dir D [--sections …] # сменить стадию: секции и смысл порядка python3 $tk init --dir D --stage build|support [--sections …] [--items …] … python3 $tk adopt scan --from … --stage S | apply --plan … # разовая адаптация, references/adopt.md ``` **Копия.** Дом словаря — `shared/axes.md` в репозитории плагина: словарь общий для всех скриптов, и ни один скилл им не владеет. Правится дом, а не этот файл. **Коды выхода — общий словарь всех скриптов `av-dev`. Ветвись на коде, а не на тексте вывода.** | Код | Что случилось | | --- | --- | | 0 | сошлось | | 1 | дрейф: рабочая ситуация, чинится | | 2 | ошибка употребления: аргументы или нарушенное правило | | 3 | окружение: не тот каталог, битый конфиг, нет инструмента | | 4 | внутренний сбой — дефект скрипта, доложить | **Различать 1 и 3 обязательно.** «Дрейф» — рабочая ситуация, и чинится она правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет. Одинаковая реакция на них неверна в обоих случаях. Здесь это значит: код 1 приходит **только от `check`** — найден дрейф индекса и файлов, чинится `check --fix`, остаток разбирается руками. Код 3 — каталог не найден, конфиг битый или мимо диска: чинится путём или `.av-dev.toml` в корне. Тип — английское ключевое слово `feature` / `fix` / `chore` / `research` (как и прочие токены команд), у `add` **обязательное**: без него неизвестно, какой шаблон тела класть. Стадия — такое же слово, `build` / `support`, и у `init` она обязательна по той же причине: без неё неизвестно, что писать в шапке беклога и сколько заводить секций. Текст задачи при этом русский, а эмодзи в заголовке ставит скрипт. **Мутации правят файл и индекс заодно** — руками строку индекса или мету не пиши, зови `add`/`edit`/`move`/`close`/`reopen`. Смена заголовка, «зачем», типа и **тегов** — это `edit`: он держит H1 (вместе с эмодзи), мету и индекс согласованными. Снятие тега — `--rm-tag` (после ответа на вопрос снимается `question`), смена типа — `--type`; оба заменяют прежнее значение, а не добавляют второе. **Секцию меняет только `move`, и он пишет причину**: смена полки без причины и есть тот дрейф, который потом никто не объяснит. **`move --after <слаг>` и `move --first` — это и есть расстановка порядка.** Порядок строк в секции значим (правило 4), и двигают его только этой командой: руками поправленная строка не оставляет причины, а причина здесь и есть половина решения. Что именно этот порядок значит, говорит стадия: на стройке `--after` называет зависимость, на доработке — приоритет. Тело задачи скрипт не трогает: `add` кладёт заголовок, мета-блок и шаблон с подсказками, тело дописываешь редактором (пока плейсхолдер на месте, `check` напоминает). `check` — единственный судья согласованности; что именно он ловит, скажет его вывод, здесь не пересказываем. Гоняй его **в начале сессии** и **после каждой правки**, даже если правил мутациями: дрейф мог накопиться раньше. Накопившееся чини `check --fix` — он детерминированно правит то, где истина однозначна (тип в своё поле, эмодзи заголовка, имя поля места, секция, заголовок, дубли, «зачем» из индекса в файл, старая форма меты, снятые теги упразднённых целей, сырьё в конец секции), а неоднозначное (нечего восстанавливать, **тип, которого неоткуда взять**, запись типа `goal`) печатает отдельной пометкой `НЕОДНОЗНАЧНО` — это тебе, и это идёт строкой доклада. **Ссылка на исчезнувший файл в пометку не попадает:** `--fix` её просто не трогает, и она остаётся `ОШИБКА` обычного `check` — то есть видна, но в докладе её надо назвать отдельно. `--fix` правит **и файлы** — там, где источник ровно один и выбирать не из чего: тип переезжает из прежнего дома (тег `kind:`, префикс `[goal]`/`[idea]`) в поле меты, заголовок получает эмодзи, поле места зовётся «Категория», «зачем», оставшееся только в индексе, переезжает в мету, теги `goal:` и `decomposed` снимаются. Каждый случай печатается поимённо. **Ни стадию, ни состав секций `--fix` не трогает.** Стадию он подставить не может — это решение человека; секции стройки не сливает — в каком порядке пойдут строки слитых полок, знает тоже только человек, а порядок здесь и есть содержание. **Тип, который не выводится ниоткуда, `--fix` не угадывает.** `feature` от `chore` машина не отличает, и подставленное наугад значение врало бы ровно там, где по нему принимают решение. Такие записи идут в `НЕОДНОЗНАЧНО`, и тип им проставляет человек — `edit <слаг> --type …`. **Что механизировано, а что нет.** Схему типа проверяет `ready` на входе в работу — там, где по ней принимают решение; `check` поимённо о ней не говорит, а считает: строка здоровья **«схема типа не выполнена: N из M»** называет число и первые слаги, строка **«готово к взятию»** — сколько задач беклога пройдут `ready` целиком (схема плюс отсутствие открытого вопроса). Это две разные строки, и совпадение их чисел — совпадение. У каждой части своя глубина: - **тип** — жёстко: назван и из закрытого словаря; - **критерии приёмки** (`feature`, `fix`, `chore`) — число пунктов жёстко (меньше двух отказ, больше пяти замечание), наличие оракула **эвристикой** по слову «оракул» в пункте; - **прочие разделы схемы** (`Затрагивает`, `Воспроизведение`, `Вопрос`, `Куда ляжет ответ`) — только **наличие непустого**. Содержимое машине не видно: границу, которую забыли назвать, она от отсутствующей не отличает, а шаги, по которым ничего не воспроизводится, — от годных. Настоящий оракул от слова «оракул» машина тоже не отличает, поэтому эвристика даёт только замечание, и в докладе это называется как есть: «проверено наличие разделов своего типа и число критериев, годность оракулов и полнота границ — глазами». Формат записи, меты, слага, индекса и `REJECTED.md` — [references/task-format.md](references/task-format.md); там же тест «готова к взятию». Схема и алгоритм каждого типа — по файлу на тип: [feature](references/task-feature.md) · [fix](references/task-fix.md) · [chore](references/task-chore.md) · [research](references/task-research.md). ## Версия раскладки Формат каталога задач меняется, и проект должен знать, к какой версии он приведён. Число живёт ключом `version` в `.av-dev.toml` в корне репозитория, журнал версий — [журнал скилла `canon`](../canon/references/changelog.md), сверяет их `tasks.py check`: отстало — строка расхождения, ушло вперёд — устарел плагин. Обратной совместимости нет: есть «приведён» и «не приведён». **Версия одна на всю раскладку — и на документы, и на задачи.** Своя у каталога задач была, пока плагинов было три и ставились они порознь: проект мог взять учёт работ без канона документов, и общее число оказалось бы домом, которого у половины проектов нет. Плагин один — довод ушёл, а два числа оставляли бы вопрос, по какому журналу повышать. **Повышает проект скилл `av-dev:canon`, операция `upgrade`** — он идёт по журналу, двигает число и зовёт этот скилл там, где запись касается задач. Здесь повышения нет намеренно: две операции, двигающие одно число, разъезжаются на первом же проекте, где прошла только одна из них. ## Сценарии ### Завести запись из диалога 0. **Посмотри стадию** — `stage`. От неё зависят шаг 1 и место новой строки: на доработке беклог пополняют по одной и с фильтром, на стройке пишут планом. 1. **Фильтр — на доработке.** Делаем прямо сейчас — не заводим. Не пожалеем о потере — не заводим. Родилось три кандидата — покажи их и спроси, какие заводить: молча заведённая пачка и есть тот самый отказ из правила 1. **На стройке фильтра нет**: план пишется вперёд целиком, и «этого мы сейчас не делаем» — не довод против шага, а описание всякого шага, кроме первого. 2. **Дедуп.** `list` плюс поиск по слагам, полю «зачем» и телам (`grep -ril`), **включая `REJECTED.md`**. Нашлось среди живых — **дописываем в существующий файл**, а не заводим соседний. Нашлось в `REJECTED.md` — покажи пользователю ту строку и что изменилось с момента отказа (`add` предупредит и сам, но молча заводить нельзя). Две задачи об одном — самая дорогая находка переоценки. 3. **Тип** — `--type` обязателен, и он же первое содержательное решение: - снаружи появляется то, чего не было → `feature`; - поведение расходится с заявленным и **воспроизводится** → `fix` (не воспроизводится → `research`); - обслуживание, наблюдаемое поведение не меняется → `chore`; - исход — знание, а не изменение системы → `research`. Не подходит ни один — в записи их два, разбирай. Не проходит тест готовности (см. task-format) — это **сырьё**: `--type research`, раздел «Вопрос» пока пуст, место в конце секции. Не делается одним заходом — дроби на шаги помельче и ставь их в списке подряд. 4. **Место в списке.** `add` кладёт строку в конец секции всегда. На стройке это почти наверняка не то место: порядок там зависимость, и новый шаг чаще всего встаёт в середину — `move <слаг> --after <слаг>`. На доработке конец списка законен: место в очереди назначает груминг, а не заведение. 5. `add …`, затем допиши тело редактором **по схеме своего типа** — шаблон её уже разложил, устав типа объясняет каждый раздел. «Зачем» отвечает «зачем нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый абзац, и пишется **для человека**: не «канонизация внутри транзакции», а «тело 40 МиБ держит блокировку 5 секунд, соседние доставки уходят в отказ». 6. `check`. ### Разобрать находки аудита или ревью Ревью и аудиты — тоже источник задач, но с опасностью, зеркальной диалогу: не пять файлов из одной мысли, а сорок файлов из сорока сырых находок. Защита та же, что в самом ревью: кластеризация по причине, дедуп против живых и `REJECTED.md`, находка без свидетельства → сырьё (`research`), а не задача, и карта кластеров пользователю до создания файлов. **Стадию смотри и здесь**, тем же нулевым шагом: от неё зависит, куда ляжет тяжёлая находка — наверх очереди или после своей зависимости. Порядок и отображение серьёзности — [references/from-review.md](references/from-review.md). ### Прийти в репозиторий, где задачи уже как-то ведутся Разовая операция: вывести каталог задач из старой раскладки беклога, `TODO.md`, заметок или списка шагов плана — [references/adopt.md](references/adopt.md). Сюда же относится переименование транслитных слагов в английские: оно делается **одним проходом вместе с починкой перекрёстных ссылок**, а не по одному слагу. Если переводить надо не только задачи, а весь `docs/` — это скилл `av-dev:canon`, и он зовёт этот сценарий сам на своём шаге. ### Пересмотр плана стройки Операция стадии `build`, и на доработке её нет: там переоценка идёт порциями и называется грумингом. **Повод один — сменился замысел**, а не «давно не смотрели»: план стройки протухает не по частям, а целиком, потому что порядок в нём — зависимость, и одна изменившаяся посылка переставляет всё, что ниже. Порционный разбор здесь вреден, и это не вкус: вынуть пять шагов из середины списка, порядок которого и есть его содержание, — значит получить план, про который никто уже не скажет, почему он такой. 1. **Назови, что изменилось в замысле.** Одной фразой, и она уедет причиной в каждое движение. Не находится — значит повода нет, и пересмотр не нужен. 2. **Прочитай список целиком**, сверху вниз, и по каждой строке ответь одно из трёх: остаётся как есть, переезжает (`move --after` с причиной), уходит (`close --reason`). Дописанное новое встаёт туда, куда велит зависимость, а не в конец. 3. **Покажи человеку весь новый список**, а не отдельные решения: план читается только целиком. `AskUserQuestion` с готовым порядком и доводом на каждое движение. 4. `check` и доклад: сколько строк тронуто из скольких, что ушло и почему. **Границу с грумингом держи твёрдо.** Если хочется пересмотреть план «потому что накопилось» — это не пересмотр, а признак того, что стройка кончилась: беклог перестал быть планом и стал очередью. Проверь `stage`. ### Декомпозиция и штурм сырья [references/split.md](references/split.md). Обе операции превращают одну запись в несколько, и у обеих есть проверяемый тест: части должны **мерджиться порознь** и **каждая давать видимую пользу**, а у штурма исход «выкинуть» — полноправный. Там же **шов**: где резать, когда допустимых мест несколько. Коротко — по границе, которая одна поднимает метку ревью выше остальных; и не резать, когда обе половины остаются в одной метке, потому что несокращаемый костяк проверок платится за каждую задачу отдельно. ### Вычитка: два прохода, а не один Записи судит **не тот агент, который их написал**: самопроверка текста слабее всего ровно там, где формулировка казалась удачной при написании. Проходов два, и они разные по природе: | Проход | Что смотрит | Над чем работает | | --- | --- | --- | | `task-form` | заголовок по типу, «зачем» вместо пересказа, границы вместо замысла, годность оракулов, предписание процесса | только `items/` | | `task-wording` | залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин, транслит в слаге | `items/` и строки индекса; документы проекта — только как словарь | Разделены они не по охвату, а **по глубине**. Язык проверяется по словам и фразам, поштучно; форма записи требует понять, что задача делает. Слитый проход одну половину делает дорогой, а вторую — поверхностной. Модель у обоих одна, `sonnet`, и это не отменяет разреза. Оба судят по **записанному правилу** — шесть пунктов формы против правил языка, — а их находка приходит готовой формулировкой, которую читает и отклоняет человек, а не молча реализует оркестратор. Ошибка здесь стоит строки чтения, и платить за неё верхней моделью не за что. Каждый устав отказывается от чужой половины прямо: увиденное не по своей части идёт **строкой в границах покрытия**, а не находкой. Две проверки одного места расходятся и начинают спорить, и разнимать их потом дороже, чем не сводить. **Порядок — сперва `task-form`.** Его находки меняют решение «брать или не брать», а язык — только цену чтения; и переписанный заголовок бессмысленно вычитывать до того, как он переписан. Зовутся они **пачкой, а не на каждую запись**: после заведения нескольких задач, после разбора находок ревью, после того как чужая работа уточнила записи (так делает разведка в `av-dev:code-resolve`), и на переоценке. Передаётся список файлов и — если есть — паспорт, архитектура и конвенции проекта: по ним отличается неизвестный термин от известного. Ни один из них ничего не правит. Оба возвращают готовые формулировки, и их подставляет скилл: заголовок — `edit <слаг> --title …`, «зачем» — `edit <слаг> --why …`, остальное редактором. **Заголовок и «зачем» — это то, по чему задачу выбирают, поэтому менять их молча нельзя**: покажи предложенное пользователю вместе с тем, что было. Правки в теле (границы, критерии, язык) применяются сразу. Всё, что ловит `tasks.py check`, оба не трогают намеренно. ### Гигиена полей Правится по ходу любой операции, которая задачи касается (но не «заодно» по всему беклогу): - **протухшее «зачем»** — задача изменилась, а поле отвечает на старый вопрос; особенно после ответа на вопрос задачи: «Решено: …» на «почему это лежит в беклоге» уже не отвечает. Переписывается `edit --why …` — он правит мету файла и строку индекса заодно; - **вопрос, застрявший в прозе** — вынимается в раздел «Вопросы» плюс тег `question` (`edit --add-tag question`), иначе он не виден ни `list --questions`, ни правилу «задача с открытым вопросом в работу не берётся»; - **тег, который некому снять** — `question` после ответа снимается `edit --rm-tag question` вместе с записью ответа в тело **и опустошением раздела «Вопросы»**: судит раздел, а не тег (`references/task-format.md`); - **свойство репозитория в рамках** — номер миграции, хеш, версия зависимости: в лежалой задаче протухает молча и становится ложной рамкой. Снимается; снимок берётся при постановке, а не при заведении; - **предписание процесса в теле** — «делать с такой-то меткой ревью», «взять такой-то агент»: это второй дом для правила выбора и путь понизить требования решением, принятым до проектирования. Снимается; - **тип, разошедшийся с задачей** — задача заводилась починкой, а после разбора оказалось, что поведение никогда и не было заявлено: это `feature`, а не `fix`. Правится `edit --type …`; тип, оставшийся от прошлой формулировки, врёт ровно там, где по нему отбирают, **и требует не тех разделов**: у брошенного `fix` останется «Воспроизведение», которого нечем заполнить; - **сырьё, у которого появился вопрос** — разведка обросла формулировкой, но раздел «Вопрос» так и пуст: она числится сырьём и в работу не берётся. Записывается вопрос, и `check --fix` поднимает строку из конца категории; - **границы, названные вместо реализации** — «переписать хранилище на новый драйвер» в разделе «Затрагивает» это не граница, а замысел. Границы — `таблица points и её миграция`, `эндпоинт POST /ingest`, `формат отпечатка на диске`. Переписывается перечнем; - **англицизм и термин из ниоткуда** — правится по ходу той же операции, что касается задачи (см. «Как написана задача»). Именно по ходу: беклог не переписывают ради языка. ## Переносимость Скилл независим от **языка программирования, сборки, CI и трекера**: он ничего не знает ни про Go, ни про npm, ни про конкретный багтрекер — задачи для него просто каталог markdown. Текст задач — русский (язык документации проекта); зашита только латиница слага. OpenSpec ему тоже не нужен. - **Каталог задач — `tasks/` в корне, жёстко**, и `--dir` передаётся явно всегда: раскладка канона одинакова во всех проектах, и искать больше нечего. Каталога нет — код 3 и вопрос человеку; `init` заводит его **только** когда проект действительно новый, а перевод чужой раскладки делает `av-dev:canon`. У скрипта поиск вверх по дереву ещё жив — он для непереведённых проектов, и полагаться на него скилл не должен: молча найденный чужой каталог это дрейф. - **Версия и настройки живут в `.av-dev.toml` в корне репозитория** — версия ключом `version`, настройки каталога секцией `[tasks]`: `dir` — где каталог лежит, `stage` — стадия проекта, плюс **имена** файлов и заголовков, и последние только если отличаются от умолчания. Неизвестный ключ в секции — код 3 на любой команде, так что лишнее слово останавливает работу с задачами целиком. Дом в корне, а не внутри каталога задач, по двум причинам: настройка, лежащая внутри настраиваемого каталога, не смогла бы сказать, **где он**; и версия одна на весь плагин, а корень есть и у проекта без `docs/`. Прежние `<каталог задач>/.tasks.json` и `docs/.docs.json` не читаются — увидев их, скрипт говорит «прежняя раскладка» и зовёт `upgrade`. - **Секции беклога** берутся из заголовков `##` индекса как есть; их названия — дело проекта (умолчание `План` на стройке, `Ядро` / `Инфра` на доработке), а количество ограничено стадией: на стройке секция одна. **В конфиге секций нет** — второй список разошёлся бы с заголовками молча. ### Вызов из другого плагина `$CLAUDE_PLUGIN_ROOT` раскрывается **только внутри своего плагина**: конвейер задачи, конвейер ревью и любой другой чужой контекст до `tasks.py` по этой переменной не дотянутся. Мост — **вызов скилла через пространство имён**, а не путь: > Чужой контекст зовёт `Skill av-dev:task-track` и называет, что нужно сделать > («закрой задачу `<слаг>`, реализована»). Скилл разрешает свой > `$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`. Заголовки, тела и «зачем» — русские. ## Чего этот скилл не делает Не пишет код, не заводит спеки и предложения об изменении, не берёт задачу в работу — этим занимается конвейер проекта. **Не ведёт очередь:** что делать следующим и что перестало быть важным — скилл `task-groom`, а этот даёт ему операции. Не решает за пользователя, что важно. Не переоформляет существующие задачи «заодно»: правится то, чего касается операция.