Команда stage была дефектна по шести пунктам, и все шесть подтверждены прогоном: не звала raw_last (переход оставлял каталог красным), не переписывала шапку беклога (индекс продолжал объявлять прежнюю стадию), шла в обход write_config, молча пропускала файлы с непересобираемой метой, ломалась на беклоге без заголовков и схлопывала полки при первом объявлении стадии. Объявление и смена разведены: объявление беклога не трогает вовсе, смена трогает состав секций только по явному --sections, а слить полки скрипт не берётся ни в одном случае. Абзац шапки размечен парой «стадия», и расхождение с конфигом стало обычным дрейфом. Отказ по недостающей строке индекса запирал запись, пережившую упразднение роадмапа: edit, close и reopen теперь заводят или пропускают строку сами. Прочее: регистр stage нормализуется при чтении; --fix снимает мёртвые теги и у неразобранных записей; move отказывает переставлять сырьё; adopt держит место сырья; docs.py bump двигает одну запись журнала за раз; tasks.py получил перечень упразднённых адресов, и гейт наконец видит собственное упразднение ROADMAP.md. Запись «Версия 3» переписана по прогону на игрушечном проекте: прежний порядок шагов был неисполним. Закрыты дыры модели стадий (пересмотр плана стройки стал сценарием, приёмка отвязана от груминга, from-review, research и adopt получили развилку по стадии, перечень осей пересчитан) и находки, старшие этой сессии: review-triage получил режим без метки, три списка проектных копий сведены к дому с проверяемыми копиями, пять пересказов правил стали помеченными копиями или ссылками, language.md перестал объявлять юрисдикцию над чужим плагином.
30 KiB
name, description
| name | description |
|---|---|
| canon | Форма раскладки проекта под av-dev и её обновление — три операции одной машиной сравнения. check — что разошлось с текущей версией раскладки; adopt — перевод проекта из любой прежней раскладки (docs/specs, drafts, backlog, BRIEF.md, review-brief) в канон с переносом файлов и вызовом владельцев каталога задач и openspec/; upgrade — повышение проекта с версии N до текущей по журналу версий, и повышается им вся раскладка, включая каталог задач. Использовать, когда просят проверить документацию проекта, перевести проект на канон, обновить его под новую версию раскладки или когда пришли в старый проект и надо понять, что в нём не так. Имя без префикса намеренно — скилл держит форму всех артефактов проекта, а не один их вид. Содержимое документов ведёт av-dev:doc-sync, форму записей задач — av-dev:task-track, заведение проекта с нуля — av-dev:doc-init. |
Форма раскладки проекта
Три операции, одна машина сравнения с разными исходами:
| Операция | Когда | Исход |
|---|---|---|
check |
начало сессии, шаг синка, гейт | что разошлось |
adopt |
проект в чужой раскладке | перенос в канон |
upgrade |
канон вырос, проект отстал | по журналу версий |
Имя без префикса, и это не случайность. Остальные скиллы названы по
материалу, с которым работают, — doc-, task-, code-; этот работает не с
материалом, а с формой, и она у всех частей проекта одна. check сверяет
раскладку документов, adopt заводит все части сразу и зовёт владельцев каталога
задач и openspec/, upgrade повышает всю раскладку одним журналом версий —
и документы, и каталог задач. Содержимое при этом не его: документы ведёт
av-dev:doc-sync, записи задач — av-dev:task-track.
Определение канона — references/canon.md. Здесь оно не пересказывается: два описания одной раскладки разъедутся, и работать будет то, которое прочитали последним. Прочитай его до первой правки.
- references/skeletons.md — что именно класть в
каждый незаполненный слот. Не выдумывай заглушку своей формы:
docs.pyузнаёт только плейсхолдер<!-- заполнить: … -->из шаблонов. - shared/language.md — как это написано словами:
информационный стиль, применённый к проектным текстам, таблицы англицизмов и
жаргона. Раскладка отвечает, где текст лежит, — этот файл отвечает, каким он
должен быть. Правила общие для документов канона, задач, решений ADR и
записок разведки, и это их дом. Вычитывают их два прохода по охвату:
документы —
doc-wording, записи каталога задач —task-wording. - references/changelog.md — журнал версий раскладки; закрытые журналы до слияния плагинов лежат рядом.
Три правила, из которых всё следует
- Сперва карта, потом файлы. Человеку показывается, что найдено, как разложилось и что не разложилось, — и только после подтверждения переносится хоть один файл. Массовый перенос без подтверждения разгребать дороже, чем согласовать.
- Ничего не терять. Содержимое переезжает целиком; ссылки чинятся тем же проходом, что и перенос. Старый файл удаляется только после того, как всё его содержимое нашло дом, и это названо поимённо.
- Что не классифицировалось — назвать. Проглоченный абзац выглядит как «всё перенеслось». Список «не разложилось» идёт в доклад целиком, с причиной по каждому пункту.
Инструмент
ds="$CLAUDE_PLUGIN_ROOT/skills/canon/scripts/docs.py"
python3 $ds check --dir <корень> [--base <rev>] # раскладка, ссылки, версия, сверки
python3 $ds version --dir <корень> # версия раскладки: скрипта и проекта
python3 $ds bump --dir <корень> # поднять версию проекта до версии скрипта
Формы openspec/config.yaml здесь больше нет. Каталог принадлежит конвейеру,
и форму смотрит его скрипт — скилл av-dev:code-openspec, команда
openspec.py check. Проект работает по OpenSpec, а каталога openspec/ нет — форму
не проверяет никто, и это надо сказать строкой доклада, а не считать, что она
верна.
Копия. Дом словаря — shared/axes.md в репозитории плагина: словарь общий
для всех скриптов, и ни один скилл им не владеет. Правится дом, а не этот файл.
Коды выхода — общий словарь всех скриптов av-dev. Ветвись на коде, а не на
тексте вывода.
| Код | Что случилось |
|---|---|
| 0 | сошлось |
| 1 | дрейф: рабочая ситуация, чинится |
| 2 | ошибка употребления: аргументы или нарушенное правило |
| 3 | окружение: не тот каталог, битый конфиг, нет инструмента |
| 4 | внутренний сбой — дефект скрипта, доложить |
Различать 1 и 3 обязательно. «Дрейф» — рабочая ситуация, и чинится она правкой предмета; «окружение» — нерабочая, и повтор той же командой не поможет. Одинаковая реакция на них неверна в обоих случаях.
Здесь это значит: «дрейф раскладки» — рабочая ситуация, «это не корень проекта» — нерабочая.
Граница механизируемого — объявляется вслух
Скрипт печатает её сам последним абзацем, и эту строку из доклада выбрасывать
нельзя. check, отчитавшийся «канон соблюдён» на проекте, где из шести файлов
три лишние, хуже отсутствующего.
Машина дрейфом считает: отсутствующий путь канона, файл вне канона, битую
ссылку, отставшую версию, capability без упоминания в обзоре, миграцию без правки
database.md. Замечанием — незаполненный плейсхолдер и слабое упоминание
capability: незаполненный канон это переходное состояние, а не отказ. Маркеры
долга просто считает числом.
Того, чего она не умеет, ты не судишь сам — для этого есть два агента, и разведены они по глубине:
| Агент | Что смотрит | Читает |
|---|---|---|
doc-consistency |
смысловой дубль, прямое противоречие между документами, поведение в architecture.md вместо спек, ADR без ссылки и парного статуса, число без провенанса, заглушка вместо честной строки |
docs/, openspec/ |
doc-code-drift |
протухший факт: имя ветки, команды, пути, зависимости поимённо, настройки с числом, единые точки проекта, capability | весь репозиторий |
Судит не тот, кто писал: самопроверка документа слабее всего ровно там, где формулировка казалась удачной при написании. Ни один из них ничего не правит — оба возвращают готовые формулировки, подставляешь ты.
Чего может не быть
adopt зовёт двоих: av-dev:code-openspec (шаг 4, пункт 3) и
av-dev:task-track (шаг 4, пункт 5). Каталоги openspec/ и tasks/ каноном не
ведутся, и трогать их этому скиллу нечем, кроме вызова.
Копия. Дом правила — shared/absence.md в репозитории плагина.
Правится дом, а не этот файл.
Скилл не вправе считать раскладку проекта полной. Части заводятся порознь и живут порознь; каждая узнаётся своим следом:
| Чего нет | Как видно | Чего теперь не делает никто |
|---|---|---|
| настройки av-dev | нет .av-dev.toml в корне |
проект под процесс не заводился; версии нет, настроек нет |
| документы канона | нет docs/ |
проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
| источник требований | нет openspec/config.yaml |
цикл SDD не запускается: спеки не с чем сверять |
Свой скилл зовётся полным именем — av-dev:canon, av-dev:task-track,
av-dev:code-review. Короткое имя может разрешиться в устаревшую проектную
копию из .claude/skills/, и подмены не будет видно ни в докладе, ни в
поведении.
Внешний плагин может не стоять. Их два: opsx:* — цикл SDD, и
av-dev-git:commit — сообщения коммитов. Путь в дерево чужого плагина не
пишется никогда: $CLAUDE_PLUGIN_ROOT ведёт только в своё дерево, а
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
сам.
Отсутствие — исход, а не поломка. Назови строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от сделанного. Выдумывать обходной путь нельзя тоже.
Присутствие узнаётся следом в проекте, а не объявлением. Перечня того, что здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
Чем оборачивается отсутствие каждого — на самих пунктах шага 4. adopt из-за
этого не останавливается ни в одном из двух случаев.
check
docs.py check, при наличии базы диффа — с--base.- Судей документов на каждом
checkне зови. Ими владеет отдельный скилл —av-dev:doc-healthcheck, — и там же записано, когда его звать: он дорог, и прогон по каждомуcheckне окупается.checkотвечает на «сходится ли форма»,doc-healthcheck— на «не разошлись ли утверждения». - Доклад: вывод скрипта строкой исхода и граница покрытия — что смотрели и
чего не смотрели. Если суждение здесь нужно, скажи это строкой и предложи
doc-healthcheck, а не зови агентов сам.
Дрейф раскладки чинится переносом; смысловые находки — это либо правка документа, либо задача, если работы больше чем на абзац.
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. Перенеси
Порядок важен — он минимизирует окно, в котором ссылки битые:
-
.av-dev.tomlв корне:version = <текущая версия>и путь миграций в[docs], если БД есть; -
каталоги канона и скелет по references/skeletons.md: незаполненное — одной честной информативной строкой, а не «TBD»;
-
OpenSpec, если его нет или
config.yamlостался примером — вызови Skillav-dev:code-openspec. Каталог принадлежит конвейеру, и команда заведения с формой файла живут там. Пересказ инвариантов, конвенций и правил ревью изcontextвычисти ссылкой на дом — на переводимом проекте он там почти наверняка есть. Проект решил жить без OpenSpec —docs.pyо каталоге тогда тоже молчит, и формуconfig.yamlне проверяет никто; скажи это строкой; -
переносы содержимого;
-
каталог задач — вызови скилл
av-dev:task-track, сценарий адаптации: он владеет форматом задач. Он же переименует транслитные слаги в английские и тем же проходом починит перекрёстные ссылки; -
починка ссылок на перенесённое во всём репозитории —
docs/,openspec/,CLAUDE.md,README.md; -
удаление оригиналов — только тех, чьё содержимое найдено в новом доме;
-
шаг
docs.py checkв гейт проекта. Путь к скрипту — переменной с умолчанием на канонический путь маркетплейса, чтобы переустановка плагина не менялаTaskfile; шаг обязан краснеть внятно, если скрипт не найден, а не пропускаться. Передай ему базу диффа (--base) той же переменной, что и остальным шагам гейта: без неё сверка миграций со схемой не гоняется вовсе. Пример строки покажи человеку — гейт принадлежит проекту, и правит его он.Шагов в гейте три, и они независимы.
docs.py checkне тянет за собой ни задачи, ни конвейер: без своих строк дрейф каталога задач и формыopenspec/config.yamlперестаёт ловиться совсем. Ставь соседние шаги по следу присутствия — каталог задач с индексом на месте, значит ставитсяtasks.py check --dir <каталог задач>;openspec/config.yamlесть, значит ставитсяopenspec.py check. Следа нет — этой части в проекте нет, шаг не ставится, и это строка доклада, а не поломка: назови, чего теперь не проверяет никто. У каждого шага своя переменная пути с тем же умолчанием на канонический путь маркетплейса;$CLAUDE_PLUGIN_ROOTв гейт не подставляй — он ведёт только в свой плагин; -
docs.py check— до отсутствия дрейфа раскладки. Замечания (незаполненные плейсхолдеры, слабое упоминание capability) остаются: незаполненный канон это объявленное переходное состояние из шага 5, а не отказ. Пересчитай эти пункты в докладе переходного состояния — не выдавай их за поломку и не молчи о них.Задачи
docs.pyне проверяет — их ведёт другой скилл, и согласованность каталога показывает толькоtasks.py check. Позвал на шаге 5 скилл задач — его отчёт идёт в доклад отдельной строкой, и зелёным он сразу не станет: у перенесённых записей нет критериев приёмки, аcheckбез объявленной стадии отказывает вовсе. Стадию называет человек (tasks.py stage build|support) — машина её не выводит: список пунктов одинаково выглядит и планом стройки, и очередью правок.
5. Объяви переходное состояние
Сразу после переноса канон заполнен не весь, и это нормально, но обязано быть названо, иначе следующий агент примет скелет за поломку.
Печатается по факту: сколько документов стоят честной строкой вместо
содержания, сколько маркеров долга в architecture.md, сколько задач без
критериев приёмки. Закрывается порциями по ходу работы, а не одним заходом.
6. Позови обоих судей
docs.py увидел раскладку, а не смысл: перенос растащил один факт по двум домам,
оставил в architecture.md поведение, которому место в спеке, и оторвал ADR от
его design.md. Ничего из этого скрипт не видит, и первый прогон на живом
проекте обычно самый урожайный — правило единственного дома до адаптации никто не
проверял.
Вызови Skill av-dev:doc-healthcheck — он зовёт обоих судей на весь канон
разом и держит разбор урожая порциями.
Передай им объявленное переходное состояние из шага 5 — иначе честная строка в незаполненном слоте вернётся находкой, а это не поломка, а объявленный долг.
7. Вычитай написанное — агент doc-wording
Судьи смотрят утверждения, а adopt только что писал текст: честные строки
в пустые слоты, переписанные при переносе абзацы, шапки перенесённых документов.
Язык этого текста не проверяет никто другой, а зовущий здесь по определению тот,
кто его и написал.
Позови агента по названной пачке — документы, которые ты завёл или правил, плюс перенесённые целиком. Весь канон ему не нужен: он работает по списку, и список же служит ему словарём терминов. Находки — готовые формулировки, подставляешь их ты.
upgrade — канон вырос
docs.py version— версия проекта и версия скрипта.- Проект новее скрипта — обнови маркетплейс, а не проект: это отстал плагин.
- Иначе иди по changelog.md снизу вверх от версии проекта до текущей и делай названное в каждой записи. Записи независимы и применяются по порядку.
- Подними версию —
docs.py bump. Он правит строку, а не переписывает файл: комментарии в нём принадлежат проекту. Последним шагом, потому что число объявляет пройденными записи журнала. docs.py check.- Позови судей — Skill
av-dev:doc-healthcheck. - Позови вычитку — агент
doc-wording, но только по тем документам, которых записи журнала коснулись, и только если правка была текстовой, а не переименованием файла. Записи журнала пишутся руками в проектной прозе, и дописанный по журналу раздел — такой же свежий текст, как на синке.
Записи журнала описывают что сделать проекту. Если запись этого не говорит — это дефект журнала, и о нём надо сказать, а не догадываться.
Каталог задач повышается этим же журналом. Версия одна на всю раскладку —
version в .av-dev.toml, — и записи журнала говорят про обе половины: и про
документы, и про каталог задач. Порознь версии жили, пока плагинов было три и
проект мог взять одну половину без другой; с одним плагином два числа означали
бы только вопрос, по какому журналу повышать. Что каталог задач отстал, скажет
tasks.py check своей строкой гейта — той же версией, что и docs.py.
Шаг 6 обязателен, и вот почему. check сверяет число в .av-dev.toml
с версией скрипта — и только его. Применена ли запись журнала по существу,
он не знает: проект несёт version текущей версии и может не иметь того, чего требовала любая
из пройденных версий. Записи применяются руками (переименовать секцию, проставить
типы, дописать раздел каждому fix), а ручной проход по нескольким записям
подряд — ровно то место, где половина шага делается и забывается. Судьи и есть
проверка, которой у upgrade иначе нет: doc-consistency увидит, что документы
разошлись после переименований, doc-code-drift — что переехавший факт
разошёлся с кодом.
Чего этот скилл не делает
- Не сочиняет содержание. Пустой слот получает честную строку о том, что его наполнить пока нечем, а не выдуманный абзац. Придуманный периметр модели угроз хуже отсутствующего: по нему будут строиться находки.
- Не удаляет то, чьё содержимое не нашло дом. Оригинал живёт, пока не названо поимённо, куда переехал каждый его кусок.
- Не ведёт содержимое канона — это скилл
doc-sync. Здесь только раскладка. - Не заводит проект с нуля — это скилл
doc-init. - Не правит историю. В старых коммитах старые пути остаются, и это нормально.
Доклад
- Что нашёл
docs.py: код выхода и число пунктов дрейфа. - Что перенесено: файл → дом, числом и поимённо для спорного.
- Удалённые дубли — с указанием, против какой спеки сверялся каждый.
- Не разложилось — поимённо, с причиной.
- Переходное состояние числами: честных строк, маркеров долга, задач без критериев.
- Граница покрытия: что проверила машина, что судил ты, чего не смотрел никто.