Сабагент в роли продакт-менеджера оценил покрытие жизненного цикла личного проекта скиллами и агентами av-dev-pm. Скоуп сужен по ходу разбора: деплой и разбор инцидентов делаются вручную, скиллов под них не заводим — три находки из восьми сняты этим сразу. Шаг 2 сессии требовал чисел, которых процесс отказался собирать решением. cadence.md делал обязанностью пересмотр «ориентира по размеру спринта, прироста беклога на закрытую задачу, времени на задачу» и «сколько заняли задачи против ожидания». Данных нет: у записи нет дат заведения, взятия и закрытия, close удаляет файл, sprint close очищает SPRINT.md. Хуже, «против ожидания» и «время на задачу» требуют оценки и тайм-бокса, а session/SKILL.md в «Почему не Scrum» их прямо не берёт — пункт противоречил решению через файл от себя. Числа не пересматривались ни разу, поэтому выкинуты, а не подперты учётом дат. Осталось качественное; рядом записано, что замеров нет намеренно, иначе следующий читатель заведёт их обратно как недостающие. Шаг 3 пункт 9 переименован из «переоценки по измеренному» в «по пройденному». doc-consistency переехал с каждого синка на сессию, к doc-code-drift. Агент на opus звался шагом 9 пайплайна, то есть 5-8 opus-проходов за спринт по документам, меняющимся на несколько абзацев. Довод сильнее денег: расхождение между двумя документами по определению требует двух, а на большинстве задач синк правит один. И пачка, отбираемая работой, не видит того, чего работа не касалась, — а расхождение живёт ровно там. Это снимает открытый вопрос REMAINING про охват парного статуса ADR. Цена — потеря привязки находки к задаче, принято сознательно. Отмена цели получила порядок, но не флаг. close запрещал закрыть цель с живыми задачами и не говорил, что с ними делать. Теперь: сперва задачи поштучно (close --reason своей причиной либо edit --goal на другую), потом цель в REJECTED.md, а не в Готово. Флаг --cascade отвергнут: поштучный разбор — не церемония, а единственный момент, когда видно, что переживёт цель. Место процедуры — переоценка на сессии, отмена цели и есть разбор её задач. У брошенного спринта появился второй законный исход. --dissolve везде был привязан к блокеру, и вернувшийся к месячному набору не имел законного хода: двигать нельзя, распускать не по чему. Теперь роспуск объясняется блокером или тем, что набор протух. Порога в неделях нет — тот же класс, что выкинутые числа: счётчик простоя пришлось бы вести руками. Признак не срок, а что набор перестал быть твоим. Плюс точка входа «вернулся, а спринт открыт» и триггер в description скилла. Журнал канона прогоняется как есть, схлопывать 3 и 4 не стали. Взамен появилась проверка исхода: шагом 6 adopt и шагом 6 upgrade зовутся оба судьи документов. Это ответ на открытый вопрос «как проверять, что канон не разошёлся с проектами после upgrade»: check сверяет число в .pm.json с версией скрипта и про существо записи не знает ничего, а записи применяются руками. Износ обязательных «границ покрытия» не правится: это гипотеза, а не находка. Записана наблюдением к первой обкатке. Предложение агента поднять обкатку выше калибровки снято — TODO уже так устроен, агент спутал «главный риск» с «первое в очереди»; в REMAINING добавлена оговорка против того же прочтения. Тема 31 в DECISIONS.md, следствия 117-123. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
19 KiB
name, description
| name | description |
|---|---|
| canon | Привести проект к канону документов av-dev и держать его в соответствии — три операции одной машиной сравнения. check — что разошлось с текущей версией канона; adopt — перевод проекта из любой прежней раскладки (docs/specs, drafts, backlog, BRIEF.md, review-brief) в канон с переносом файлов; upgrade — повышение проекта с версии канона N до текущей по журналу версий. Использовать, когда просят проверить документацию проекта, перевести проект на канон, обновить его под новую версию канона или когда пришли в старый проект и надо понять, что в нём не так. Заведение нового проекта с нуля — скилл init. |
Приведение проекта к канону
Три операции, одна машина сравнения с разными исходами:
| Операция | Когда | Исход |
|---|---|---|
check |
начало сессии, шаг синка, гейт | что разошлось |
adopt |
проект в чужой раскладке | перенос в канон |
upgrade |
канон вырос, проект отстал | по журналу версий |
Определение канона — references/canon.md. Здесь оно не пересказывается: два описания одной раскладки разъедутся, и работать будет то, которое прочитали последним. Прочитай его до первой правки.
- references/skeletons.md — что именно класть в
каждый незаполненный слот. Своей формой заглушку не выдумывай:
docs.pyузнаёт только плейсхолдер<!-- заполнить: … -->из шаблонов. - references/language.md — как это написано словами:
информационный стиль, применённый к проектным текстам, таблицы англицизмов и
жаргона. Раскладка отвечает, где текст лежит, — этот файл отвечает, каким он
должен быть. Правила общие для документов канона, задач, решений ADR и
записок разведки; вычитывает их отдельным проходом агент
doc-wording. - references/changelog.md — журнал версий канона.
Три правила, из которых всё следует
- Сперва карта, потом файлы. Человеку показывается, что найдено, как разложилось и что не разложилось, — и только после подтверждения переносится хоть один файл. Массовый перенос без подтверждения разгребать дороже, чем согласовать.
- Ничего не терять. Содержимое переезжает целиком; ссылки чинятся тем же проходом, что и перенос. Старый файл удаляется только после того, как всё его содержимое нашло дом, и это названо поимённо.
- Что не классифицировалось — назвать. Проглоченный абзац выглядит как «всё перенеслось». Список «не разложилось» идёт в доклад целиком, с причиной по каждому пункту.
Инструмент
ds="$CLAUDE_PLUGIN_ROOT/skills/canon/scripts/docs.py"
python3 $ds check --dir <корень> [--base <rev>] # раскладка, ссылки, версия, сверки
python3 $ds version --dir <корень> # версия канона скрипта и проекта
Коды выхода — тот же словарь, что у tasks.py: 0 сошлось, 1 дрейф, 2 ошибка
употребления, 3 окружение, 4 внутренний сбой. Ветвись на коде, а не на тексте.
Различать 1 и 3 обязательно: «дрейф раскладки» — рабочая ситуация, «это не корень проекта» — нерабочая.
Граница механизируемого — объявляется вслух
Скрипт печатает её сам последним абзацем, и эту строку из доклада выбрасывать
нельзя. check, отчитавшийся «канон соблюдён» на проекте, где из шести файлов
три лишние, хуже отсутствующего.
Машина дрейфом считает: отсутствующий путь канона, файл вне канона, битую
ссылку, отставшую версию, capability без упоминания в обзоре, миграцию без правки
database.md. Замечанием — незаполненный плейсхолдер и слабое упоминание
capability: незаполненный канон это переходное состояние, а не отказ. Маркеры
долга просто считает числом.
Того, чего она не умеет, ты не судишь сам — для этого есть два агента, и разведены они по глубине:
| Агент | Что смотрит | Читает |
|---|---|---|
doc-consistency |
смысловой дубль, прямое противоречие между документами, поведение в architecture.md вместо спек, ADR без ссылки и парного статуса, число без провенанса, заглушка вместо честной строки |
docs/, openspec/ |
doc-code-drift |
протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки проекта, capability | весь репозиторий |
Судит не тот, кто писал: самопроверка документа слабее всего ровно там, где формулировка казалась удачной при написании. Ни один из них ничего не правит — оба возвращают готовые формулировки, подставляешь ты.
check
docs.py check, при наличии базы диффа — с--base.- Агентов на каждом
checkне зови. Оба —doc-consistencyиdoc-code-drift— зовутся раз в спринт (шаг сессии), а также шагом 6adoptи шагом 6upgrade, на весь канон разом. Они дороги: оба наopus, второй ещё и читает репозиторий. Позвалdoc-code-drift— передай ему раздел запретовCLAUDE.md. - Доклад: вывод скрипта строкой исхода, находки агентов поимённо, граница покрытия — что смотрели и чего не смотрели, и кто из двоих был позван: доклад, умолчавший об этом, читается как «сверено».
Дрейф раскладки чинится переносом; смысловые находки — это либо правка документа, либо задача, если работы больше чем на абзац.
adopt — проект в чужой раскладке
1. Осмотрись
docs.py check — он уже назовёт упразднённые слоты с адресом, куда каждый
уезжает. Но смотрит он только верхний уровень docs/: упразднённое в корне
репозитория (BRIEF.md) и во вложенных каталогах он не назовёт никогда, поэтому
корневые *.md читаются глазами. Плюс: CLAUDE.md, openspec/specs/ (список
capability), openspec/config.yaml.
2. Составь карту
Каждый найденный файл получает строку: куда едет, целиком или разбирается, что
делать с оригиналом. Разбор docs/specs/ — самое дорогое место, и он делается
поимённо по capability:
| Что в файле | Куда |
|---|---|
| требования, сценарии, поведение | openspec/specs/<capability>/spec.md — или уже там, тогда файл дубль |
| компоненты, транспорты, раскладка, деплой | docs/architecture.md |
| конвенции чужой системы, формат чужих данных | docs/research/ |
| обоснование принятого решения | docs/adr/ |
Дубль удаляется только после поимённой сверки: открыть спеку capability, открыть файл, убедиться, что в файле нет ничего сверх спеки. Нашлось сверх — сперва переезжает в спеку дельтой, потом файл удаляется.
3. Покажи карту человеку
AskUserQuestion, не больше трёх вопросов за итерацию, рекомендация первым
вариантом. Показывается: сколько файлов, куда каждый, спорные отнесения, список
«не разложилось». Механику (порядок строк, имена файлов внутри research/) не
выноси — это не развилка.
4. Перенеси
Порядок важен — он минимизирует окно, в котором ссылки битые:
docs/.pm.jsonс{"canon": <текущая версия>}и путём миграций, если БД есть;- каталоги канона и скелет по references/skeletons.md: незаполненное — одной честной информативной строкой, а не «TBD»;
- переносы содержимого;
- каталог задач — вызови скилл
av-dev-pm:tasks, сценарий адаптации: он владеет форматом задач, включая переименование транслитных слагов в английские вместе с починкой перекрёстных ссылок; - починка ссылок на перенесённое во всём репозитории —
docs/,openspec/,CLAUDE.md,README.md; - удаление оригиналов — только тех, чьё содержимое найдено в новом доме;
- шаг
docs.py checkв гейт проекта. Путь к скрипту — переменной с умолчанием на канонический путь маркетплейса, чтобы переустановка плагина не менялаTaskfile; шаг обязан краснеть внятно, если скрипт не найден, а не пропускаться. Передай ему базу диффа (--base) той же переменной, что и остальным шагам гейта: без неё сверка миграций со схемой не гоняется вовсе. Пример строки покажи человеку — гейт принадлежит проекту, и правит его он; docs.py check— до отсутствия дрейфа раскладки. Замечания (незаполненные плейсхолдеры, слабое упоминание capability) остаются: незаполненный канон это объявленное переходное состояние из шага 5, а не отказ. Пункт «задачи без цели» из вложенной проверкиtasks.pyтоже остаётся и зелёным на этом шаге не станет: цели не сочиняются адаптацией (запрет в tasks/references/adopt.md), их проставляет человек порциями переоценки на первой сессии. Пересчитай эти пункты в докладе переходного состояния — не выдавай их за поломку и не молчи о них.
5. Объяви переходное состояние
Сразу после переноса канон заполнен не весь, и это нормально, но обязано быть названо, иначе следующий агент примет скелет за поломку.
Печатается по факту: сколько документов стоят честной строкой вместо
содержания, сколько маркеров долга в architecture.md, сколько задач без
критериев приёмки. Закрывается порциями по ходу работы, а не одним заходом.
6. Позови обоих судей
docs.py увидел раскладку, а не смысл: перенос растащил один факт по двум домам,
оставил в architecture.md поведение, которому место в спеке, и оторвал ADR от
его design.md. Ничего из этого скрипт не видит, и первый прогон на живом
проекте обычно самый урожайный — правило единственного дома до адаптации никто не
проверял.
Зови doc-consistency (документы между собой и с openspec) и
doc-code-drift (факты против кода). Разбирай порциями, а не одним заходом.
Передай им объявленное переходное состояние из шага 5 — иначе честная строка в незаполненном слоте вернётся находкой, а это не поломка, а объявленный долг.
upgrade — канон вырос
docs.py version— версия проекта и версия скрипта.- Проект новее скрипта — обнови маркетплейс, а не проект: это отстал плагин.
- Иначе иди по changelog.md снизу вверх от версии проекта до текущей и делай названное в каждой записи. Записи независимы и применяются по порядку.
- Подними
canonвdocs/.pm.jsonдо текущей. docs.py check.- Позови обоих судей —
doc-consistencyиdoc-code-drift.
Записи журнала описывают что сделать проекту. Если запись этого не говорит — это дефект журнала, и о нём надо сказать, а не догадываться.
Шаг 6 обязателен, и вот почему. check сверяет число в .pm.json с
версией скрипта — и только его. Применена ли запись журнала по существу, он
не знает: проект несёт "canon": 4 и может не иметь того, чего требовала любая
из пройденных версий. Записи применяются руками (переименовать секцию, проставить
типы, дописать раздел каждому fix), а ручной проход по нескольким записям
подряд — ровно то место, где половина шага делается и забывается. Судьи и есть
проверка, которой у upgrade иначе нет: doc-consistency увидит, что документы
разошлись после переименований, doc-code-drift — что переехавший факт
разошёлся с кодом.
Чего этот скилл не делает
- Не сочиняет содержание. Пустой слот получает честную строку о том, что его наполнить пока нечем, а не выдуманный абзац. Придуманный периметр модели угроз хуже отсутствующего: по нему будут строиться находки.
- Не удаляет то, чьё содержимое не нашло дом. Оригинал живёт, пока не названо поимённо, куда переехал каждый его кусок.
- Не ведёт содержимое канона — это скилл
docs. Здесь только раскладка. - Не заводит проект с нуля — это скилл
init. - Не правит историю. В старых коммитах старые пути остаются, и это нормально.
Доклад
- Что нашёл
docs.py: код выхода и число пунктов дрейфа. - Что перенесено: файл → дом, числом и поимённо для спорного.
- Удалённые дубли — с указанием, против какой спеки сверялся каждый.
- Не разложилось — поимённо, с причиной.
- Переходное состояние числами: честных строк, маркеров долга, задач без критериев.
- Граница покрытия: что проверила машина, что судил ты, чего не смотрел никто.