--- name: tasks description: Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). Заведение задачи, идеи или цели из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, мозговой штурм идеи, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Ритуал между спринтами — скилл session. Не реализует задачи — этим занимается пайплайн проекта. --- # Задачи Задачи — каталог markdown-файлов. Одна запись = один файл `items/.md` плюс строка **ровно в одном** индексе. Скилл владеет **форматом и содержимым**: заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует идеи. Чем он **не** владеет: ритуалом между спринтами (разбор вопросов → разбор прошедшего спринта → переоценка → выбор цели и набор) — это скилл `session`; и выполнением задачи — это пайплайн проекта. ## Пять правил, из которых всё следует Ситуация не покрыта инструкцией — решай по ним. 0. **Цель — возможность приложения, задача — шаг к ней.** Цель отвечает на «что приложение будет уметь», её «Завершение» — наблюдаемый признак того, что уже умеет; задача отвечает на «что для этого нужно сделать». Оценивается проект по **поведению**, а не по внутреннему устройству, поэтому и роадмап отвечает не «сколько работ осталось», а «что уже умеет и чего ещё не умеет». Свойство поведения — тоже возможность: «сообщает о своём состоянии», «исход слияния не зависит от порядка доставки» — законные цели. 1. **Беклог гниёт с той стороны, где его пополняют.** Заведение — самая частая операция и с худшим отказом: из одного разговора рождается пять файлов, а переоценка потом разгребает то, чего не надо было заводить. Дедупликация и фильтр на входе дешевле любой чистки. Заводим только то, что **не делаем сейчас** и о потере чего пожалеем. 2. **Файл — источник истины, индексы производны.** Разошлись — неправы индексы. Согласованность механизируема и проверяется командой, а не вниманием: всё, что ловит `tasks.py check`, не должно попадать ни в чек-лист, ни в промпт. Поэтому **«зачем» живёт в мете файла**, а строка индекса его лишь повторяет: пока поле лежало только в индексе, восстановление пропавшей строки теряло его молча и навсегда. Единственное исключение намеренное: **в каком индексе лежит задача, знают индексы** — «в спринте» это свойство спринта, а не файла, поля-состояния нет. 3. **Причина переживает запись.** Выкинутая без причины задача вернётся через квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не оставляет ничего, поэтому у неё есть `REJECTED.md`. 4. **Порядка нет, есть цель — но цель есть не у всякой задачи.** Приоритетов, «повысить» и «встать раньше» нет: «что делать дальше» отвечает набор спринта, а между спринтами порядок не нужен никому. Цель обязательна там, где она и есть содержание работы, — у **новой возможности** (`kind:feature`). Починка, техдолг и разведка служат работоспособности, а не направлению, и живут без цели законно; в набор спринта они входят помимо его цели. Придуманная им цель — то же враньё, от которого спасает род работы. ## Раскладка Каталог задач — **`docs/tasks`, жёстко**: это часть [канона документов](../canon/references/canon.md), и подгоняется под него проект, а не наоборот. ``` docs/tasks/ items/ задачи и цели файлами, .md, слаги английские ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет BACKLOG.md что можно взять — только задачи, целей здесь нет SPRINT.md текущий спринт: цель, набор, дата REJECTED.md ушедшее БЕЗ реализации, с причиной и датой ``` Правило, снимающее путаницу: **`BACKLOG.md` — то, что берут; `ROADMAP.md` — то, подо что берут.** Цель в спринт взять нельзя, поэтому в списке берущихся ей не место. **Четыре секции роадмапа, и первая отвечает на половину вопроса:** | Секция | Англ. | Что в ней | | --- | --- | --- | | `Готово` | `Done` | достигнутые цели — строкой с датой, **без ссылки на файл**: файл удалён, поведение живёт в спеках | | `Запланировано` | `Planned` | очередь значима и обосновывается прозой рядом | | `Направления` | `Directions` | очереди нет, тянутся долго | | `Разработка` | `Tooling` | инструмент и процесс — не возможности приложения, и потому отдельно | **Секции роадмапа канонические, секции беклога — нет**, и разница не в любви к единообразию. У каждой секции роадмапа свой смысл, в первую пишет сам `close`, и роадмап, названный по-своему, читался бы только своим автором. Секции беклога (`Ядро`, `Инфра`) смысла не несут — это полки, и остаются делом проекта. Отсюда три правила, которые проверяет `tasks.py check`: **состав закреплён** (чужая секция — ошибка, а не вольность), **все четыре обязаны быть** (нет секции — нет ответа на её часть вопроса), **язык один на весь индекс**. `--roadmap-sections` у `init` нет: выбирать нечего. **Заголовок секции — с прописной, после него пустая строка.** Во всех индексах одинаково, включая секции беклога, которые проект называет сам. Написание канонических секций правит `check --fix` (заодно и ссылку на секцию в мете файлов: имя секции принадлежит заголовку индекса, файл на неё только ссылается); отбивку он ставит везде. Оговорка про `Разработка`: слово `окружение` сюда не годится — в `architecture.md` оно уже значит боевое окружение приложения, и одно слово в двух смыслах развело бы документы канона. **Секции «блокеры» в беклоге нет.** Блокер — это *состояние* (спринт не может продолжаться ни одной задачей), а не полка: он живёт ровно до ответа человека, и записи в такой секции не успевают жить. Следы блокера остаются вопросами в файлах задач распущенного спринта. Постоянно пустая секция со старой семантикой «разбираются пачками» противоречила бы правилу «блокер эскалируется немедленно», поэтому `init` её заводить отказывается, а `check` о ней говорит. **Проекту, который переезжает с такой секцией, её надо удалить** — это единственное место, где это сказано. **Задача живёт в одном индексе за раз.** Взята в спринт — строка переезжает из `BACKLOG.md` в `SPRINT.md`; вышла — обратно. Файл в `items/` при этом **не двигается**: он и есть запись, индексы лишь показывают, где она числится. **У сделанной задачи записи не остаётся** — файл и строка удаляются (`close --implemented`). Ей хватает коммита и документации проекта; вторая запись была бы вторым домом для того же факта. Вопрос «что было в спринте N» отвечается даром: `SPRINT.md` лежит под git, `git log -p docs/tasks/SPRINT.md` отдаёт историю всех наборов без отдельного журнала. **У достигнутой цели запись остаётся, и это единственное исключение.** Файл удаляется так же, а строка переезжает в секцию `Готово` с датой. Причина в том, что цель — не работа, а **возможность**: «что приложение умеет» это половина вопроса, ради которого роадмап и открывают, и стирать её вместе с файлом значит оставить инструмент, отвечающий только «что осталось». Вторым домом это не становится: поведение живёт в `openspec/specs/`, а роадмап отвечает **когда и в каком порядке оно появилось** — другой вопрос. Ссылки на файл в строке нет намеренно: файл удалён, а битая ссылка — законная ошибка `check`. Куда запись может переехать и какой командой — весь набор переходов: ```mermaid stateDiagram-v2 state "BACKLOG.md — что берут" as B state "ROADMAP.md — подо что берут" as P state "SPRINT.md — набор спринта" as S state "REJECTED.md — ушла без реализации" as R state "записи нет — реализована" as D state "ROADMAP.md, «умеет» — цель достигнута" as A [*] --> B: add [*] --> P: add --type goal B --> P: edit --type goal --section P --> B: edit --type task --section B --> S: sprint take S --> B: sprint drop --reason S --> D: close --implemented P --> A: close --implemented B --> R: close --reason S --> R: close --reason P --> R: close --reason D --> B: reopen --reason R --> B: reopen --reason A --> P: reopen --reason ``` Состояния здесь — **где числится строка**, а не где лежит файл: файл `items/.md` не двигается ни на одном переходе. Стрелок «руками» на схеме нет намеренно — каждый переход это команда, и другого способа его совершить не существует. Схема — **сводка**: условия и оговорки живут в тексте разделов, и при расхождении прав текст. ## Цели **Цель — возможность приложения.** Такой же файл в `items/`, тип `[goal]`, перечисленный в `ROADMAP.md`. Формулируется ответом на вопрос **«что приложение будет уметь»**, а не названием области работ: не «Работа с чтением», а «Чтение данных клиентами»; не «Рефакторинг слияния», а «Исход слияния не зависит от порядка доставки». **Свойство поведения — тоже возможность.** «Наблюдаемость» это «приложение сообщает о своём состоянии»; «прочность слияния» это «исход не зависит от порядка». Такие цели законны и переформулировки в функцию не требуют — требуют только, чтобы формулировка отвечала на «что приложение делает», а не на «какую часть кода мы трогаем». **Что целью не является — работа над станком.** Инструмент, процесс, сборка, сам этот скилл: на вопрос «что приложение будет уметь» они не отвечают. Им отведена отдельная секция роадмапа, чтобы они были видны в том же экране и при этом не читались как возможности продукта. Секция выбирается так: очередь значима и обоснована прозой — `Запланировано`; тянется долго и очереди не имеет — `Направления`; не про приложение — `Разработка`; в `Готово` кладёт сам `close`. - **Список задач цели выводится, а не хранится.** В теле цели — зачем она и что считается её завершением; перечня задач там нет. Он был бы третьим индексом и поехал бы на первой же закрытой задаче, а `check` про него не знает. Связь однонаправленна: задача несёт тег `goal:<слаг>`, перечень даёт `tasks.py list --goal <слаг>`. - **Статус цели выводится.** Цель достигнута, когда у неё не осталось открытых задач; `[x]`/`[~]` руками не ведутся, а `close` цели с живыми задачами скрипт запретит. Достижение — `close <цель> --implemented`: файл удаляется, строка с датой переезжает в `Готово`. Ошиблись — `reopen` вернёт файл и **снимет строку достигнутого**, чтобы роадмап не утверждал того, чего нет. Единственная оговорка: цель без задач неотличима — «ещё не разобрана» или «всё закрыто». Различает **тег `decomposed`** в мете цели: он ставится, когда цель разложена на задачи. Тег, а не строка в теле — потому что проверяется механически: `check` **напоминает** о нём у пустой цели (замечанием, не ошибкой — неразобранная цель это законное состояние), а `check --fix` сам проставляет его цели, у которой задачи есть. - **Тип `[epic]` упразднён.** Он был зонтиком между целью и задачами — «задача, которая не мерджится целиком». Зонтик теперь цель, а слишком крупный шаг просто дробится на шаги помельче под той же целью, и промежуточному типу места не осталось. Замер подтвердил: ноль употреблений на 97 записей двух живых проектов. Встретился в чужом беклоге — это цель либо набор задач, и `check` назовёт его неизвестным типом. ## Род работы **Тип записи и род работы — две оси, и путать их нельзя.** Тип отвечает «что это за запись» (цель, идея, задача), род — «какого рода работа»: `feature`, `fix`, `chore`, `research`. Одним значением на оба вопроса не ответить: идея бывает *про* функцию, а цель функцией *и является*. - **`feature`** — снаружи появляется или меняется то, чего раньше не было. - **`fix`** — поведение расходится с заявленным, и расхождение воспроизводится. Не воспроизводится — это `research`, а не `fix`. - **`chore`** — обслуживание: зависимости, сборка, перенос, чистка. Наблюдаемое поведение не меняется, и в этом всё дело: **у `chore` тест готовности слабее честно**, а не молча. «Что станет наблюдаемо иначе» здесь отвечается разработчику («перестанет собираться два раза», «уедет последний вызов устаревшего API»), а не пользователю. Пока рода не было, такие задачи либо не заводились, либо формулировались как выдуманная польза. - **`research`** — исход работы знание, а не изменение системы: ответ на вопрос, замер, разведка. Приёмка — записанный ответ (`docs/research/`, ADR, тело задачи), а не изменённый код. Дом рода — **тег `kind:<род>`**, а не префикс заголовка и не поле меты: теги здесь единственный механизм разметки, и `list --kind fix` работает даром. Цена известна: в строку индекса род не попадает (индексы производны), и «в наборе одни починки» видно командой, а не глазами по `SPRINT.md`. Словарь **закрыт**. Открытый разъедется на синонимах — `bug`, `bugfix`, `fix`, `defect`, — и отбор по роду перестанет отвечать на свой единственный вопрос. Ни один род не подходит — это сигнал, что в задаче их два и её надо разделить. **Род обязателен у задачи, у цели запрещён, у идеи необязателен** — идея получает его, когда становится задачей. Требуется он там, где по нему принимают решение: `sprint take` без рода откажет. `check` о пропаже только **напоминает** — беклог, заведённый до появления рода, законен, и переоформлять его «заодно» здесь не просят. **Род решает и то, обязательна ли цель.** `feature` без цели не бывает: новая возможность и есть содержание цели, и если подходящей нет — либо она заводится, либо это не `feature`. `fix`, `chore` и `research` живут без цели законно, и `check` о них молчит: они служат работоспособности, а не направлению. Это единственный случай, когда род что-то определяет за пределами отбора, — и определяет он учёт, а не процесс проверки. **Род не выбирает профиль ревью и вообще ничего не предписывает пайплайну.** Профиль выбирается по факту изменения, а не по роду задачи: `chore` бывает миграцией схемы, `fix` — правкой публичного контракта. Правило «предписание процесса в теле задачи снимается» родом не отменяется, а подтверждается: он описывает работу, а не то, как её проверять. ## Как написана задача Три требования к тексту. Первое — про заголовок, два остальных про то, чтобы задачу можно было **оценить, не открывая код**. **Заголовок отвечает на вопрос своего типа.** Вопросов три, поэтому и форм три: | Тип | Отвечает на | Пример | | --- | --- | --- | | цель | что приложение будет уметь | Соперником может быть компьютер | | задача | что нужно сделать | Печатать поле одним куском кода | | идея | о чём она | Подсказка следующего хода | Задача — **глаголом в неопределённой форме**, перед ним допускается «не»: «Не отбрасывать молча лишние символы в ходе», а не «Лишние символы молча отбрасываются». Описательный заголовок называет **состояние**, а из состояния не видно, чего от работы ждут: «Ничья объявляется, пока клетки есть» одинаково читается и как жалоба, и как задание, — и в списке, где решают «брать или не брать», это разные вещи. Идея формы действия не несёт **намеренно**: что делать, ещё неизвестно, и заголовок-действие обещал бы решённость, которой нет. Из этого же правила растёт разница индексов: роадмап — список возможностей, беклог — список работ, и если заголовки перепутать формами, каждый из них начинает читаться как другой. `check` считает заголовки не в форме действия и печатает **число** в блоке здоровья, не замечанием на файл: проверка эвристическая (первое слово на `-ть`/`-ти`/`-чь`), а беклог, заведённый до правила, не переоформляют «заодно». Годность формулировки — не машине: её смотрит [агент вычитки](#вычитка-формулировок). **Функции и границы, а не намерения.** Задача называет, что система начнёт делать, и какие границы это трогает: эндпоинт или команду, таблицу и миграцию, формат на диске, публичный тип пакета, внешний сервис. Перечень живёт разделом «Затрагивает» (форма — [references/task-format.md](references/task-format.md)) и требуется к взятию в спринт. Без него задача оценивается по объёму текста, а не по объёму поверхности, — и оценка систематически занижена ровно там, где текст короткий, а границ много. Названы **границы**, а не то, как они изменятся: план реализации живёт в предложении об изменении, а не в задаче. **Предметно, но без усложнения.** Текст задачи читает человек, который решает, брать её или нет, и делает это по строке индекса и одному экрану тела. - **англицизм, у которого есть русское слово, — заменяется**: не «зафиксить флоу», а «починить порядок доставки»; не «отрефакторить», а «убрать второй путь приёма». Английские остаются там, где они и есть имя вещи: слаг, `capability`, имя пакета, команда, тип в коде. - **термин, которого нет в паспорте, архитектуре или конвенциях проекта, вводится одной строкой** или не употребляется. Свой словарь у задачи — самый дешёвый способ сделать беклог нечитаемым для того, кто вернётся к нему через квартал. - **сложность формулировки — не признак сложности работы.** Задачу, которую не удаётся сказать просто, чаще всего не удаётся и оценить: это либо две задачи, либо идея. Эти правила — про **язык**, а не про объём: короткая задача без границ хуже длинной с ними. ## Инструмент (`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] [--kind K] [--tag a,b] [--goal S] [--index …] [--questions] python3 $tk add --dir D --slug S --title T [--type goal|idea] [--section S] [--goal G] [--kind K] [--why «зачем»] [--tag a,b] python3 $tk edit S --dir D [--title T] [--why «зачем»] [--type T] [--goal G] [--kind K] [--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 …] [--roadmap-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` / `task` (как и прочие токены команд); `task` префикса не несёт, остальные кодируются `[goal]`/`[idea]` в заголовке. Текст задачи при этом русский. **Мутации правят файл и индексы заодно** — руками строку индекса или мету не пиши, зови `add`/`edit`/`move`/`close`/`sprint`. Смена заголовка, «зачем», типа, цели, рода работы и **тегов** — это `edit`: он держит H1, мету и индекс в синхроне. Снятие тега — `--rm-tag` (после ответа на вопрос снимается `question`), смена цели — `--goal`, рода — `--kind`; оба заменяют прежнее значение, а не добавляют второе. **Переезд между индексами — следствие смены типа, а не отдельная команда.** `edit --type goal --section <часть роадмапа>` переносит строку из `BACKLOG.md` в `ROADMAP.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 goal`). 4. **Цель задачи — если род её требует.** У `feature` должен быть `--goal <слаг>`: новая возможность и есть содержание цели. Подходящей нет — либо она заводится (`--type goal`), либо перед тобой не `feature`. У `fix`, `chore` и `research` цели может не быть вовсе, и придумывать её не надо. У идеи цель проставляется, когда идея становится задачей. 5. **Род работы** — `--kind feature|fix|chore|research` (см. «Род работы»). Не подходит ни один — задача не одна, разбирай. 6. `add …`, затем допиши тело редактором: одна фраза, **затрагиваемые границы**, критерии приёмки с оракулами, рамки. «Зачем» отвечает «зачем нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый абзац, и пишется **для человека**: не «канонизация внутри транзакции», а «тело 40 МиБ держит блокировку 5 секунд, соседние доставки уходят в отказ». 7. `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). Обе операции превращают одну запись в несколько, и у обеих есть проверяемый тест: части должны **мерджиться порознь** и **каждая давать видимую пользу**, а у штурма исход «выкинуть» — полноправный. Там же **шов**: где резать, когда допустимых мест несколько. Коротко — по границе, которая одна поднимает ступень ревью выше остальных; и не резать, когда обе половины остаются в одной ступени, потому что несокращаемый костяк проверок платится за каждую задачу отдельно. ### Вычитка формулировок Язык записей судит **отдельный проход** — агент `task-wording`, а не тот же агент, который их только что написал: самопроверка текста слабее всего ровно там, где формулировка казалась удачной при написании. Зовётся он **пачкой, а не на каждую запись**: после заведения нескольких задач, после разбора находок ревью и на переоценке. Ему передаётся список файлов и — если есть — паспорт, архитектура и конвенции проекта: по ним он отличает неизвестный термин от известного. Он ничего не правит. Возвращает готовые формулировки, и их подставляет скилл: заголовок — `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 --kind …`; род, оставшийся от прошлой формулировки, врёт ровно там, где по нему отбирают; - **границы, названные вместо реализации** — «переписать хранилище на новый драйвер» в разделе «Затрагивает» это не граница, а замысел. Границы — `таблица points и её миграция`, `эндпоинт POST /ingest`, `формат отпечатка на диске`. Переписывается перечнем; - **англицизм и термин из ниоткуда** — правится по ходу той же операции, что касается задачи (см. «Как написана задача»). Именно по ходу: беклог не переписывают ради языка. ## Переносимость Скилл независим от **языка программирования, сборки, 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`. Не решает за пользователя, что важно. Не переоформляет существующие задачи «заодно»: правится то, чего касается операция.