Первый прогон агента — по репозиторию, который его же и содержит. Два прохода, 17 находок, все подтверждены по файлам. Пять находок — остатки прежней модели типов в файлах, до которых я не дошёл двумя коммитами раньше. adopt.md держал имена секций роадмапа канона 2 («порядка», «темы») и «пустой goal законен только у идеи»; from-review.md и TODO.md — упразднённый [idea]; task-batch в другом плагине — «задачи-идеи». Правку модели я вёл от документов, которые менял, а не от тех, что на них ссылаются: grep по упразднённому слову дал бы все пять за минуту. Самая дорогая находка оказалась моей и свежей. Таблица типов в canon.md объявляла цель у fix запрещённой, а tasks/SKILL.md и task-fix.md — необязательной; код на стороне вторых. Копия разошлась с домом за один день, обе половины писал один проход. Поправлено не значение, а причина: canon.md дважды объявлял, что фиксирует только словарь типов, — значит колонкам «разделы» и «цель» в нём не место. Осталась таблица из двух колонок и ссылка на дом схемы. Перечень «чем держат проект» пересказывался втроём и разъехался: «метрики и логи» против «мониторинга», «проверки» есть в двух из трёх. При этом tasks/SKILL.md ссылался на дом рядом с собственным пересказом — ссылка не мешает копии разойтись, если копия всё равно стоит. Перечень остался в canon.md, два места ссылаются. README пересказывал раскладку канона блоком кода, и копия была уже неполна — не хватало путей, чьё отсутствие docs.py считает нарушением. Заменено ссылкой. Там же измеренное число из DECISIONS III заменено ссылкой на решение. REMAINING дублировал два отмеченных сделанными пункта TODO и держал счётчики, которые обязан двигать человек: «двенадцати тем и 16 коммитов» (стало 28 и 52), «три неизмеренных изменения» (стало больше). Счётчики отменены как класс, причина записана в шапку. Открытый вопрос про парный статус ADR переформулирован: судья появился, открыт остался охват. Три противоречия вне av-dev-pm: --roadmap-sections перечислен среди флагов init прозой того же файла, объявляющей, что его нет; review-ops берёт журнал docs/review.md и тут же объявляет историю инцидентов принципиально недоступной; «честный предел» конвейера отменял целиком документ docs/research/. Плюс битый якорь ссылки на раздел вычитки. Находка про Co-Authored-By снята как неверная: агент прочитал av-dev-git/skills/commit/SKILL.md как описание практики этого репозитория, а это продукт, уезжающий в чужие проекты. Устав агента не различает «документ про нас» и «документ про то, что мы производим» — остаток записан в REMAINING, в устав пока не дописан. DECISIONS тема 29 (ЧЧШШ–ЮЮЯЯ, следствия 109–113). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
63 KiB
name, description
| name | description |
|---|---|
| tasks | Ведение задач и целей как каталога markdown-файлов (одна запись = один файл в items/ + строка в одном из индексов). У каждой записи есть тип (goal, feature, fix, chore, research), и тип решает, каких разделов она требует и что с ней можно делать. Заведение записи из диалога, разбор находок аудита/ревью, декомпозиция на независимо полезные части, штурм сырья, гигиена полей и проверка согласованности индексов. Использовать, когда просят добавить задачу/идею/цель, превратить находки ревью в задачи, разбить задачу, проработать идею, поправить формат или проверить беклог. Ритуал между спринтами — скилл session. Не реализует задачи — этим занимается пайплайн проекта. |
Задачи
Задачи — каталог markdown-файлов. Одна запись = один файл items/<slug>.md плюс
строка ровно в одном индексе. Скилл владеет форматом и содержимым:
заводит, редактирует, закрывает, разбирает находки ревью, дробит, штурмует сырьё.
Чем он не владеет: ритуалом между спринтами (разбор вопросов → разбор
прошедшего спринта → переоценка → выбор цели и набор) — это скилл session; и
выполнением задачи — это пайплайн проекта.
Шесть правил, из которых всё следует
Ситуация не покрыта инструкцией — решай по ним.
-
Цель — возможность приложения, задача — шаг к ней. Цель отвечает на «что приложение будет уметь», её «Завершение» — наблюдаемый признак того, что уже умеет; задача отвечает на «что для этого нужно сделать». Оценивается проект по поведению, а не по внутреннему устройству, поэтому и роадмап отвечает не «сколько работ осталось», а «что уже умеет и чего ещё не умеет». Свойство поведения — тоже возможность: «сообщает о своём состоянии», «исход слияния не зависит от порядка доставки» — законные цели.
-
Беклог гниёт с той стороны, где его пополняют. Заведение — самая частая операция и с худшим отказом: из одного разговора рождается пять файлов, а переоценка потом разгребает то, чего не надо было заводить. Дедупликация и фильтр на входе дешевле любой чистки. Заводим только то, что не делаем сейчас и о потере чего пожалеем.
-
Файл — источник истины, индексы производны. Разошлись — неправы индексы. Согласованность механизируема и проверяется командой, а не вниманием: всё, что ловит
tasks.py check, не должно попадать ни в чек-лист, ни в промпт. Поэтому «зачем» живёт в мете файла, а строка индекса его лишь повторяет: пока поле лежало только в индексе, восстановление пропавшей строки теряло его молча и навсегда. Единственное исключение намеренное: в каком индексе лежит задача, знают индексы — «в спринте» это свойство спринта, а не файла, поля-состояния нет. -
Причина переживает запись. Выкинутая без причины задача вернётся через квартал тем же текстом. Реализованная оставляет след в коммите — выкинутая не оставляет ничего, поэтому у неё есть
REJECTED.md. -
Порядка нет, есть цель — но цель есть не у всякой задачи. Приоритетов, «повысить» и «встать раньше» нет: «что делать дальше» отвечает набор спринта, а между спринтами порядок не нужен никому. Цель обязательна там, где она и есть содержание работы, — у новой возможности (
feature). Починка, техдолг и разведка служат работоспособности, а не направлению, и живут без цели законно; в набор спринта они входят помимо его цели. Придуманная им цель — то же враньё, от которого спасает тип.Единственный порядок, который в беклоге всё-таки есть, производен от типа, а не назначен человеком: сырьё (
researchбез раздела «Вопрос») стоит в конце своей категории. Его не берут, и между берущимся оно каждый раз требует открыть файл, чтобы это понять. Раз порядок выводится, его проверяет машина — и приоритетом он не становится. -
Тип решает, что с записью можно делать. Тип — единственная ось и первое поле меты: от него зависят обязательные разделы тела, нужна ли цель, берётся ли запись в спринт и в каком индексе живёт её строка. Словарь закрыт; ни один тип не подошёл — значит, в записи их два, и её надо разделить.
Раскладка
Каталог задач — docs/tasks, жёстко: это часть
канона документов, и подгоняется под него
проект, а не наоборот.
docs/tasks/
items/ задачи и цели файлами, <slug>.md, слаги английские
ROADMAP.md состояние проекта: что уже умеет и чего ещё не умеет
BACKLOG.md что можно взять — только задачи, целей здесь нет
SPRINT.md текущий спринт: цель, набор, дата
REJECTED.md ушедшее БЕЗ реализации, с причиной и датой
Правило, снимающее путаницу: BACKLOG.md — то, что берут; ROADMAP.md — то,
подо что берут. Цель в спринт взять нельзя, поэтому в списке берущихся ей не
место.
Четыре секции роадмапа, и последняя отвечает на половину вопроса:
| Секция | Англ. | Что в ней |
|---|---|---|
Запланировано |
Planned |
очередь значима и обосновывается прозой рядом |
Направления |
Directions |
очереди нет, тянутся долго |
Сопровождение |
Operations |
чем держат проект: инструмент, процесс, эксплуатация — не возможности приложения, и потому отдельно |
Готово |
Done |
достигнутые цели — строкой с датой, без ссылки на файл: файл удалён, поведение живёт в спеках |
Порядок тоже канонический, и Готово стоит последним не из скромности.
Достигнутое копится: через год этой секции больше, чем всех остальных
вместе. Стоя первой, она отодвигает за экран ровно то, ради чего роадмап
открывают чаще всего, — что делается сейчас и что дальше. Порядок проверяет
check, переставляет check --fix.
Секции роадмапа канонические, категории беклога — нет, и разница не в любви к
единообразию. У каждой секции роадмапа свой смысл, в достигнутое пишет сам 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.
Куда запись может переехать и какой командой — весь набор переходов:
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 --type feature|fix|chore|research
[*] --> P: add --type goal
B --> P: edit --type goal --section
P --> B: edit --type feature|fix|chore|research --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/<slug>.md не двигается ни на одном переходе. Стрелок «руками» на схеме
нет намеренно — каждый переход это команда, и другого способа его совершить не
существует.
Схема — сводка: условия и оговорки живут в тексте разделов, и при расхождении прав текст.
Цели
Цель — возможность приложения. Такой же файл в items/, тип goal (🎯),
перечисленный в ROADMAP.md. Формулируется ответом на вопрос «что приложение
будет уметь», а не названием области работ: не «Работа с чтением», а «Чтение
данных клиентами»; не «Рефакторинг слияния», а «Исход слияния не зависит от
порядка доставки».
Свойство поведения — тоже возможность. «Наблюдаемость» это «приложение сообщает о своём состоянии»; «прочность слияния» это «исход не зависит от порядка». Такие цели законны и переформулировки в функцию не требуют — требуют только, чтобы формулировка отвечала на «что приложение делает», а не на «какую часть кода мы трогаем».
Что целью не является — работа, которой держат проект. Состав перечислен в каноне, раздел «Сопровождение и эксплуатация»; на вопрос «что приложение будет уметь» ничто из него не отвечает. Им отведена отдельная секция роадмапа, чтобы они были видны в том же экране и при этом не читались как возможности продукта.
Граница проходит по тому, кто наблюдает, а не по теме. «Приложение сообщает о своём состоянии» — возможность: наблюдает пользователь сервиса, и цели место среди прочих. «Дежурный видит состояние на одном экране» — сопровождение: наблюдаем мы. Одна и та же наблюдаемость попадает в разные секции, и это верно — секции отвечают на разные вопросы.
Сопровождение и эксплуатация — целое и часть, а не синонимы, и та же тема
живёт ещё в двух местах канона: разделе «Эксплуатация» в architecture.md и
эксплуатационном проходе ревью. Словарь у всех трёх общий и живёт одним домом —
canon.md, раздел «Сопровождение и эксплуатация».
Пересказывать его здесь нельзя: три перечня «чем держат проект» уже разъезжались
на «метриках и логах» против «мониторинга».
Секция выбирается так: очередь значима и обоснована прозой — Запланировано;
тянется долго и очереди не имеет — Направления; не про приложение, а про то,
чем его держат, — Сопровождение; в Готово кладёт сам close.
- Список задач цели выводится, а не хранится. В теле цели — зачем она и что
считается её завершением; перечня задач там нет. Он был бы третьим индексом и
поехал бы на первой же закрытой задаче, а
checkпро него не знает. Связь однонаправленна: задача несёт тегgoal:<слаг>, перечень даётtasks.py list --goal <слаг>. - Статус цели выводится. Цель достигнута, когда у неё не осталось открытых
задач;
[x]/[~]руками не ведутся, аcloseцели с живыми задачами скрипт запретит. Достижение —close <цель> --implemented: файл удаляется, строка с датой переезжает вГотово. Ошиблись —reopenвернёт файл и снимет строку достигнутого, чтобы роадмап не утверждал того, чего нет. Единственная оговорка: цель без задач неотличима — «ещё не разобрана» или «всё закрыто». Различает тегdecomposedв мете цели: он ставится, когда цель разложена на задачи. Тег, а не строка в теле — потому что проверяется механически:checkнапоминает о нём у пустой цели (замечанием, не ошибкой — неразобранная цель это законное состояние), аcheck --fixсам проставляет его цели, у которой задачи есть. - Тип
[epic]упразднён. Он был зонтиком между целью и задачами — «задача, которая не мерджится целиком». Зонтик теперь цель, а слишком крупный шаг просто дробится на шаги помельче под той же целью, и промежуточному типу места не осталось. Замер подтвердил: ноль употреблений на 97 записей двух живых проектов. Встретился в чужом беклоге — это цель либо набор задач, иcheckназовёт его неизвестным типом.
Тип записи
Тип — единственная ось, и он решает, что с записью можно делать. Дом типа —
поле меты Тип первой строкой; эмодзи в заголовке H1 от него производна, её
ставит add и чинит check --fix.
| Тип | Обязательные разделы | Цель | В спринт | Устав |
|---|---|---|---|---|
🎯 goal |
Завершение |
— | нет | task-goal.md |
✨ feature |
Затрагивает, Критерии приёмки |
обязательна | да | task-feature.md |
🐞 fix |
Воспроизведение, Затрагивает, Критерии приёмки |
необязательна | да | task-fix.md |
🧹 chore |
Затрагивает, Критерии приёмки |
нет | да | task-chore.md |
🔬 research |
Вопрос, Куда ляжет ответ |
нет | да | task-research.md |
Сверх обязательных у любой задачи допустимы Рамки и Вопросы. Раздел не из
схемы своего типа — замечание, а не ошибка: свой раздел законная вольность
проекта, но Воспроизведение у chore почти всегда значит, что тип проставлен
не тот, и сказать об этом стоит, не запрещая.
Осей было две, и ортогональность у них была фальшивой. Тип записи
(goal/idea/task) и род работы (kind:<род> тегом) давали двенадцать клеток
произведения, из которых законны были шесть: у цели род запрещён, у задачи
обязателен, у идеи пуст. Плюс алгоритм работы крепится не к task, а к fix и
research — то есть к роду. Оси схлопнуты, тег kind: упразднён.
Тип idea упразднён вместе с ними. Он значил не род работы, а состояние
незаполненности — «первый, второй или третий вопрос теста готовности не
отвечается», — а состояние типом быть не может: оно меняется по мере того, как
запись дописывают, а тип меняют командой. Теперь это состояние называется честно:
research без раздела «Вопрос» — сырьё. В спринт не берётся ровно как
прежняя идея, лежит в конце своей категории и отбирается list --raw.
Словарь закрыт. Открытый разъедется на синонимах — bug, bugfix, fix,
defect, — и отбор по типу перестанет отвечать на свой единственный вопрос. Ни
один тип не подходит — это сигнал, что в задаче их два и её надо разделить.
Требуется тип там, где по нему принимают решение: sprint take без типа
откажет, потому что не знает, каких разделов требовать. check о пропаже только
напоминает — беклог, заведённый до появления типа, законен, и переоформлять
его «заодно» здесь не просят.
Тип не выбирает профиль ревью и вообще ничего не предписывает пайплайну.
Профиль выбирается по факту изменения, а не по типу задачи: chore бывает
миграцией схемы, fix — правкой публичного контракта. Правило «предписание
процесса в теле задачи снимается» типом не отменяется, а подтверждается: он
описывает работу, а не то, как её проверять.
Как написана задача
Три требования к тексту. Первое — про заголовок, два остальных про то, чтобы задачу можно было оценить, не открывая код.
Заголовок отвечает на вопрос своего типа. Вопросов три, поэтому и форм три:
| Тип | Отвечает на | Пример |
|---|---|---|
🎯 goal |
что приложение будет уметь | Соперником может быть компьютер |
✨ feature, 🐞 fix, 🧹 chore |
что нужно сделать | Печатать поле одним куском кода |
🔬 research |
о чём разведка | Подсказка следующего хода |
Задача — глаголом в неопределённой форме, перед ним допускается «не»: «Не
отбрасывать молча лишние символы в ходе», а не «Лишние символы молча
отбрасываются». Описательный заголовок называет состояние, а из состояния не
видно, чего от работы ждут: «Ничья объявляется, пока клетки есть» одинаково
читается и как жалоба, и как задание, — и в списке, где решают «брать или не
брать», это разные вещи. research формы действия не несёт намеренно: её
исход знание, что делать — ещё неизвестно, и заголовок-действие обещал бы
решённость, которой нет.
Из этого же правила растёт разница индексов: роадмап — список возможностей, беклог — список работ, и если заголовки перепутать формами, каждый из них начинает читаться как другой.
check считает заголовки не в форме действия и печатает число в блоке
здоровья, не замечанием на файл: проверка эвристическая (первое слово на
-ть/-ти/-чь), а беклог, заведённый до правила, не переоформляют «заодно».
Годность формулировки — не машине: её смотрит
агент вычитки.
Функции и границы, а не намерения. Задача называет, что система начнёт делать, и какие границы это трогает: эндпоинт или команду, таблицу и миграцию, формат на диске, публичный тип пакета, внешний сервис. Перечень живёт разделом «Затрагивает» (форма — references/task-format.md) и требуется к взятию в спринт. Без него задача оценивается по объёму текста, а не по объёму поверхности, — и оценка систематически занижена ровно там, где текст короткий, а границ много. Названы границы, а не то, как они изменятся: план реализации живёт в предложении об изменении, а не в задаче.
Предметно, но без усложнения. Текст задачи читает человек, который решает, брать её или нет, и делает это по строке индекса и одному экрану тела.
Язык — общий для всех проектных текстов, и живёт он одним файлом: ../canon/references/language.md (информационный стиль, применённый к задачам и документам канона; там же таблицы англицизмов и жаргона и то, что из стиля отброшено намеренно). Задаче он даёт четыре требования, которые нарушаются чаще прочих:
- глагол вместо отглагольного существительного: «обработчик не проверяет владельца», а не «проверка владельца не осуществляется»;
- факт вместо оценки: «время ответа доходит до 800 мс», а не «работает медленно». Оценка без факта рядом — настроение, а не сведение;
- англицизм с живым русским аналогом заменяется: не «зафиксить флоу», а
«починить порядок доставки». Имя вещи не переводится: слаг, команда, тип в
коде,
API; - термин не из документов проекта вводится одной строкой или не употребляется. Свой словарь у задачи — самый дешёвый способ сделать беклог нечитаемым для того, кто вернётся к нему через квартал.
И одно требование, которое есть только у задачи: сложность формулировки — не признак сложности работы. Задачу, которую не удаётся сказать просто, чаще всего не удаётся и оценить: это либо две задачи, либо сырьё.
Эти правила — про язык, а не про объём: короткая задача без границ хуже длинной с ними.
Инструмент (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] [--raw] [--index …] [--questions]
python3 $tk add --dir D --slug S --title T --type goal|feature|fix|chore|research [--section S] [--goal G] [--why «зачем»] [--tag a,b]
python3 $tk edit S --dir D [--title T] [--why «зачем»] [--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 …] [--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 / feature / fix / chore /
research (как и прочие токены команд), у add обязательное: без него
неизвестно, какой шаблон тела класть. Текст задачи при этом русский, а эмодзи в
заголовке ставит скрипт.
Мутации правят файл и индексы заодно — руками строку индекса или мету
не пиши, зови add/edit/move/close/sprint. Смена заголовка, «зачем», типа,
цели и тегов — это edit: он держит H1 (вместе с эмодзи), мету и индекс в
синхроне. Снятие тега — --rm-tag (после ответа на вопрос снимается
question), смена цели — --goal, типа — --type; оба заменяют прежнее
значение, а не добавляют второе.
Переезд между индексами — следствие смены типа, а не отдельная команда.
edit <slug> --type goal --section <часть роадмапа> переносит строку из
BACKLOG.md в ROADMAP.md (и обратно — задачным типом плюс
--section <категория беклога>);
move двигает только внутри одного индекса и пишет причину. --section у
edit работает только при таком переезде — иначе он отсылает к move,
потому что смена секции без причины и есть тот дрейф, который потом никто не
объяснит. Задача в наборе спринта тип не меняет вовсе: сперва sprint drop.
Тело задачи скрипт не трогает:
add кладёт заголовок, мета-блок и шаблон с подсказками, тело дописываешь
редактором (пока плейсхолдер на месте, check напоминает).
check — единственный судья согласованности; что именно он ловит, скажет его
вывод, здесь не пересказываем. Гоняй его в начале сессии и после каждой
правки, даже если правил мутациями: дрейф мог накопиться раньше. Накопившееся
чини check --fix — он детерминированно правит то, где истина однозначна (тип в
своё поле, эмодзи заголовка, имя поля места, секция, заголовок, дубли, «зачем» из
индекса в файл, старая форма меты, пометка decomposed у цели с задачами, сырьё
в конец категории), а неоднозначное (задача сразу в двух индексах, нечего
восстанавливать, тип, которого неоткуда взять) печатает отдельной пометкой
НЕОДНОЗНАЧНО — это тебе, и это идёт строкой доклада. Ссылка на исчезнувший
файл в пометку не попадает: --fix её просто не трогает, и она остаётся
ОШИБКА обычного check — то есть видна, но в докладе её надо назвать отдельно.
--fix правит и файлы — там, где источник ровно один и выбирать не из чего:
тип переезжает из прежнего дома (тег kind:, префикс [goal]/[idea]) в поле
меты, заголовок получает эмодзи, поле места — имя по типу, «зачем», оставшееся
только в индексе, переезжает в мету, цель с задачами получает decomposed.
Каждый случай печатается поимённо.
Тип, который не выводится ниоткуда, --fix не угадывает. feature от
chore машина не отличает, и подставленное наугад значение врало бы ровно там,
где по нему принимают решение. Такие записи идут в НЕОДНОЗНАЧНО, и тип им
проставляет человек — edit <слаг> --type ….
Что механизировано, а что нет. У задачи, взятой в набор (sprint take и
check по задачам спринта), проверяется схема её типа, и у каждой части своя
глубина:
- тип — жёстко: назван и из закрытого словаря;
- критерии приёмки (
feature,fix,chore) — число пунктов жёстко (меньше двух отказ, больше пяти замечание), наличие оракула эвристикой по слову «оракул» в пункте; - прочие разделы схемы (
Затрагивает,Воспроизведение,Вопрос,Куда ляжет ответ,Завершение) — только наличие непустого. Содержимое машине не видно: границу, которую забыли назвать, она от отсутствующей не отличает, а шаги, по которым ничего не воспроизводится, — от годных.
Настоящий оракул от слова «оракул» машина тоже не отличает, поэтому эвристика даёт только замечание, и в докладе это называется как есть: «проверено наличие разделов своего типа и число критериев, годность оракулов и полнота границ — глазами».
Формат записи, меты, слага, индексов и REJECTED.md —
references/task-format.md; там же тест «готова к
взятию». Схема и алгоритм каждого типа — по файлу на тип:
goal · feature ·
fix · chore ·
research.
Сценарии
Завести запись из диалога
-
Фильтр. Делаем прямо сейчас — не заводим. Не пожалеем о потере — не заводим. Родилось три кандидата — покажи их и спроси, какие заводить: молча заведённая пачка и есть тот самый отказ из правила 1.
-
Дедуп.
listплюс поиск по слагам, полю «зачем» и телам (grep -ril), включаяREJECTED.md. Нашлось среди живых — дописываем в существующий файл, а не заводим соседний. Нашлось вREJECTED.md— покажи пользователю ту строку и что изменилось с момента отказа (addпредупредит и сам, но молча заводить нельзя). Две задачи об одном — самая дорогая находка переоценки. -
Тип —
--typeобязателен, и он же первое содержательное решение:- возможность приложения, а не шаг к ней →
goal; - снаружи появляется то, чего не было →
feature; - поведение расходится с заявленным и воспроизводится →
fix(не воспроизводится →research); - обслуживание, наблюдаемое поведение не меняется →
chore; - исход — знание, а не изменение системы →
research.
Не подходит ни один — в записи их два, разбирай. Не проходит тест готовности (см. task-format) — это сырьё:
--type research, раздел «Вопрос» пока пуст, место в конце категории. Не делается одним заходом — это не эпик, а несколько задач под одной целью: дроби сразу. - возможность приложения, а не шаг к ней →
-
Цель — если тип её требует. У
featureдолжен быть--goal <слаг>: новая возможность и есть содержание цели. Подходящей нет — либо она заводится (--type goal), либо перед тобой неfeature. Уfix,choreиresearchцели может не быть вовсе, и придумывать её не надо. -
add …, затем допиши тело редактором по схеме своего типа — шаблон её уже разложил, устав типа объясняет каждый раздел. «Зачем» отвечает «зачем нужна эта задача» — состояние, остаток, боль, — а не пересказывает первый абзац, и пишется для человека: не «канонизация внутри транзакции», а «тело 40 МиБ держит блокировку 5 секунд, соседние доставки уходят в отказ». -
check.
Разобрать находки аудита или ревью
Ревью и аудиты — тоже источник задач, но с зеркальной диалогу опасностью: не
пять файлов из одной мысли, а сорок файлов из сорока сырых находок. Защита та
же, что в самом ревью: кластеризация по причине, дедуп против живых и
REJECTED.md, находка без свидетельства → сырьё (research), а не задача, и карта кластеров
пользователю до создания файлов. Порядок, отображение серьёзности и привязка к
целям — references/from-review.md.
Прийти в репозиторий, где задачи уже как-то ведутся
Разовая операция: вывести каталог задач из старой раскладки беклога, TODO.md,
заметок или списка шагов роадмапа — references/adopt.md.
Сюда же относится переименование транслитных слагов в английские: оно делается
одним проходом вместе с починкой перекрёстных ссылок, а не по одному слагу.
Если переводить надо не только задачи, а весь docs/ — это скилл
av-dev-pm:canon, и он зовёт этот сценарий сам на своём шаге.
Декомпозиция и штурм сырья
references/split.md. Обе операции превращают одну запись в несколько, и у обеих есть проверяемый тест: части должны мерджиться порознь и каждая давать видимую пользу, а у штурма исход «выкинуть» — полноправный.
Там же шов: где резать, когда допустимых мест несколько. Коротко — по границе, которая одна поднимает ступень ревью выше остальных; и не резать, когда обе половины остаются в одной ступени, потому что несокращаемый костяк проверок платится за каждую задачу отдельно.
Вычитка: два прохода, а не один
Записи судит не тот агент, который их написал: самопроверка текста слабее всего ровно там, где формулировка казалась удачной при написании. Проходов два, и они разные по природе:
| Проход | Что смотрит | Над чем работает |
|---|---|---|
task-form |
заголовок по типу, «зачем» вместо пересказа, границы вместо замысла, годность оракулов, предписание процесса, связь со строкой «Завершения» цели | только items/, открывает файл цели |
doc-wording |
залог и отглагольные, оценка без факта, стоп-слова, англицизмы, жаргон, неизвестный термин | любой проектный текст, включая документы канона |
Разделены они не по охвату, а по глубине. Язык проверяется по словам и фразам, поштучно; форма записи требует понять, что задача делает, и открыть цель, на которую она ссылается. Слитый проход одну половину делает дорогой, а вторую — поверхностной. Отсюда и разные модели.
Каждый устав отказывается от чужой половины прямо: увиденное не по своей части идёт строкой в границах покрытия, а не находкой. Две проверки одного места расходятся и начинают спорить, и разнимать их потом дороже, чем не сводить.
Порядок — сперва task-form. Его находки меняют решение «брать или не
брать», а язык — только цену чтения; и переписанный заголовок бессмысленно
вычитывать до того, как он переписан.
Зовутся они пачкой, а не на каждую запись: после заведения нескольких задач, после разбора находок ревью и на переоценке. Передаётся список файлов и — если есть — паспорт, архитектура и конвенции проекта: по ним отличается неизвестный термин от известного.
Ни один из них ничего не правит. Оба возвращают готовые формулировки, и их
подставляет скилл: заголовок — edit <слаг> --title …, «зачем» —
edit <слаг> --why …, остальное редактором. Заголовок и «зачем» — это то, по
чему задачу выбирают, поэтому менять их молча нельзя: покажи предложенное
пользователю вместе с тем, что было. Правки в теле (границы, критерии, язык)
применяются сразу.
Всё, что ловит tasks.py check, оба не трогают намеренно.
Гигиена полей
Правится по ходу любой операции, которая задачи касается (но не «заодно» по всему беклогу):
- протухшее «зачем» — задача изменилась, а поле отвечает на старый вопрос;
особенно после ответа на вопрос задачи: «Решено: …» на «почему это лежит в
беклоге» уже не отвечает. Переписывается
edit <slug> --why …— он правит мету файла и строку индекса заодно; - вопрос, застрявший в прозе — вынимается в раздел «Вопросы» плюс тег
question(edit --add-tag question), иначе он не виден ниlist --questions, ни правилу «задача с открытым вопросом в набор не берётся»; - тег, который некому снять —
questionпосле ответа снимаетсяedit --rm-tag questionвместе с записью ответа в тело и опустошением раздела «Вопросы»: судит раздел, а не тег (references/task-format.md); - свойство репозитория в рамках — номер миграции, хеш, версия зависимости: в лежалой задаче протухает молча и становится ложной рамкой. Снимается; снимок берётся при постановке, а не при заведении;
- предписание процесса в теле — «делать таким-то профилем ревью», «взять такой-то агент»: это второй дом для правила выбора и путь понизить требования решением, принятым до проектирования. Снимается;
- тип, разошедшийся с задачей — задача заводилась починкой, а после разбора
оказалось, что поведение никогда и не было заявлено: это
feature, а неfix. Правитсяedit <slug> --type …; тип, оставшийся от прошлой формулировки, врёт ровно там, где по нему отбирают, и требует не тех разделов: у брошенногоfixостанется «Воспроизведение», которого нечем заполнить; - сырьё, у которого появился вопрос — разведка обросла формулировкой, но
раздел «Вопрос» так и пуст: она числится сырьём и в спринт не берётся.
Записывается вопрос, и
check --fixподнимает строку из конца категории; - границы, названные вместо реализации — «переписать хранилище на новый
драйвер» в разделе «Затрагивает» это не граница, а замысел. Границы —
таблица 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:
- Что такое «сделана» — чем задача выполняется (пайплайн проекта) и что входит в его определение готовности. Скилл требует лишь форму: пайплайн проекта пройден + критерии приёмки проверены поимённо.
- Что считается необратимым и потому спрашивается у человека всегда (деплой, выкладка наружу, удаление или перезапись данных).
Ни того ни другого скилл не угадывает: не нашёл — спрашивает пользователя, а не подставляет умолчание.
Общее для всех сценариев
- Развилки — пользователю. Через
AskUserQuestion, с уже сформулированным предварительным суждением (рекомендация — первым вариантом). Что выкинуть, под какую цель отнести, какая рамка разведки верна — решение пользователя. Слаг, формулировка, порядок строк в индексе — механика, делаем сами. - Не больше трёх вопросов за раз. Пачка длиннее трёх тяжела для ответа; решений больше — веди несколько итераций диалога по ≤3, а не один перегруженный запрос. Между итерациями применяй уже решённое.
- Границы покрытия в отчёте. Любая сессия разбора, штурма или интейка заканчивается строкой «просмотрено N из M, не трогали — …». Отчёт без неё сообщает «беклог разобран», не сообщая, какая его часть осталась нетронутой.
- Ничего не удаляем молча. Файл исчезает только через
close—--reason(ушла без реализации) или--implemented(реализована). Прямогоrmнет. - Слаги английские, kebab-case, не транслит:
tie-break-equal-completeness, а неtaj-brejk-pri-ravnoj-polnote. Заголовки, тела и «зачем» — русские.
Чего этот скилл не делает
Не пишет код, не заводит спеки и предложения об изменении, не берёт задачу в
работу — этим занимается пайплайн проекта. Не ведёт спринт и не проводит сессию
между спринтами — это session. Не решает за пользователя, что важно. Не
переоформляет существующие задачи «заодно»: правится то, чего касается операция.