Files
dev-skills/av-dev-pm/skills/canon/SKILL.md
T
avandClaude Opus 5 d5bee11a6b классификация задачи: три категории документов и метка вместо ступени
Канон 5 объявил «каждый документ docs/ — тема ревью». Правило верно ровно
наполовину и потому вредно целиком. Паспорт и схему хранилища ревью читает, но
темами они не являются: по ним нельзя сказать «в этом изменении сделано не так»,
они задают границу, по которой судит чужая тема. Журнал решений и журнал
наблюдений ревью изменения не нужны вовсе — ADR объясняет прошлое, а не
предъявляет требование. Разметчик, применявший правило буквально, обязан был
либо завести фантомные темы passport, adr, database, research и продублировать
ими работу architecture и operations, либо потерять четыре документа молча;
случались обе ветки, и в собственном образце плана docs/passport.md не попадал
ни строкой, а обязательная арифметика покрытия при этом не сходилась.

Категорий теперь три, разрез проверяемый. Тема — да, прямо: conventions,
security, architecture и любой свой документ проекта. Источник темы — нет, но он
задаёт границу для чужой: passport, database, CLAUDE.md, openspec/specs.
Процессный — нет, он про то, как мы работаем: tasks, review, adr, research,
.pm.json. Открыта одна категория из трёх, две другие перечислены поимённо, так
что документ вне раскладки — однозначно своя тема. adr и research прогон больше
не открывает ни одним проходом; docs/review остаётся читаемым, но как настройка
конвейера, а не критерий. Цена записана и стала обязательной строкой границ
покрытия: расхождение с записанным решением ловит теперь только сверка
документации, а число под находкой обязано быть снято на этом прогоне, с
приложенной командой.

Классификация выдаёт задаче метку — small, medium, large. Прежние quick,
standard и wide назывались ступенью и описывали ревью: как глубоко смотрим.
Классифицируется же задача, и пока величина называлась свойством прогона, её
естественно было пересчитывать на каждом прогоне — что конвейер и делал. Слово
«ступень» удалено, а не оставлено синонимом: два имени одной вещи расходятся.
Выводится метка из двух разведённых осей — размер (малое, среднее, крупное) и
сложность (знакомое, незнакомое), — и равна максимуму по ним. Метка не синоним
размера: малое незнакомое изменение получает large, трогая один узел, поэтому
план печатает три строки с обоснованием каждая и выводить одну из другой
запрещено. Оси остались русскими словами — это суждение прозой; метка
английская — это идентификатор, который проходы сравнивают.

Разметка переехала из ревью кода в шаг 4 пайплайна, сразу после propose. Она
шла первым проходом каждого ревью кода, а перед ревью дизайна ту же величину
называл сам пайплайн — то есть оркестратор, который только что довёл
предложение до propose. Одно и то же измерялось дважды, и один из двух раз без
разведённости с автором, ровно в той точке, ради которой разметчик заведён.
Теперь запуск один на задачу, диффа он не видит, план обслуживает обе стадии, и
метка после кода не пересматривается: расхождение факта с разметкой ловит журнал
дефектов постфактум, как и всякую другую ошибку выбора. На диск план не пишется —
четвёртый артефакт рядом с proposal, tasks и design пережил бы задачу и разошёлся
бы с ней молча.

Ревью дизайна тоже растёт меткой: small — specs, medium — плюс rubric, large —
плюс architecture и вопрос автору о трёх формах решения. Раньше rubric и
architecture включались одним условием, и medium получал ровно один проход, то
есть не отличался от quick ничем. Разведены они потому, что зарабатывают на
разном: рубрика порождает свойства узла и окупается уже на среднем изменении,
её выход уезжает приёмочными критериями в tasks.md; архитектура отвечает на
вопрос про второй способ, а он на среднем знакомом изменении отвечается «нет»
ещё до запуска.

small подешевел тремя способами сразу. Составом: приёмник тем не запускается,
три темы ядра переходят к code сверкой по записанным инвариантам CLAUDE.md с
потолком в одну находку, и это не «глубина ниже», а другой дом темы. Входом:
specs читает только дельта-спеку, code — только индекс конвенций. Потолком: он
появился у каждого опиниативного прохода, а не у одного basics, и у половин code
он раздельный, потому что конвенционных находок больше по построению и в общем
списке они вытеснили бы техническую половину. Сработавший потолок обязан быть
объявлен строкой — молчащий срез неотличим от «больше не нашлось». Отрицательный
тест small от этого стал жёстче, а не мягче: вопросы про обратимость миграции
задавал приёмник тем, и на этой метке их не задаст никто.

Пайплайн задачи вырос до двенадцати шагов. Тривиальность перестала решать состав
ревью — она влияет только на explore; глубину обеих стадий называет метка.

Проверено прогоном ревьюверов по готовому результату: девять расхождений найдено
и починено — контракт находок печатал старый перечень проходов вместо плана по
темам, три ссылки в task-batch указывали на шаг коммита вместо закрытия, запись
changelog не переводила вопросы, адресованные passport и database, ops и
adversary утверждали, что на нижних метках их вопросы задаёт basics, шаблон
покрытия в review-code зашивал потолки small намертво, триггеры метки рассыпались
на два списка против трёх, тема из директивы CLAUDE.md могла остаться без запуска
исполнителя. Гейт зелёный: фронтматтеры, копии, одиннадцать диаграмм, ruff,
pyrefly; docs.py прогнан на живом фикстуре и печатает категорию в отказе.

Канон повышен до версии 6 с записью, выполнимой upgrade. Решения — 40–44.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:57:15 +03:00

19 KiB
Raw Blame History

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 — журнал версий канона.

Три правила, из которых всё следует

  1. Сперва карта, потом файлы. Человеку показывается, что найдено, как разложилось и что не разложилось, — и только после подтверждения переносится хоть один файл. Массовый перенос без подтверждения разгребать дороже, чем согласовать.
  2. Ничего не терять. Содержимое переезжает целиком; ссылки чинятся тем же проходом, что и перенос. Старый файл удаляется только после того, как всё его содержимое нашло дом, и это названо поимённо.
  3. Что не классифицировалось — назвать. Проглоченный абзац выглядит как «всё перенеслось». Список «не разложилось» идёт в доклад целиком, с причиной по каждому пункту.

Инструмент

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

  1. docs.py check, при наличии базы диффа — с --base.
  2. Агентов на каждом check не зови. Оба — doc-consistency и doc-code-drift — зовутся раз в спринт (шаг сессии), а также шагом 6 adopt и шагом 6 upgrade, на весь канон разом. Они дороги: doc-consistency — тем, что на opus (сличение утверждений это суждение), doc-code-drift — тем, что читает репозиторий целиком, хотя сам идёт на sonnet. Позвал doc-code-drift — передай ему раздел запретов CLAUDE.md.
  3. Доклад: вывод скрипта строкой исхода, находки агентов поимённо, граница покрытия — что смотрели и чего не смотрели, и кого из двоих позвал: доклад, умолчавший об этом, читается как «сверено».

Дрейф раскладки чинится переносом; смысловые находки — это либо правка документа, либо задача, если работы больше чем на абзац.

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. Перенеси

Порядок важен — он минимизирует окно, в котором ссылки битые:

  1. docs/.pm.json с {"canon": <текущая версия>} и путём миграций, если БД есть;
  2. каталоги канона и скелет по references/skeletons.md: незаполненное — одной честной информативной строкой, а не «TBD»;
  3. переносы содержимого;
  4. каталог задач — вызови скилл av-dev-pm:tasks, сценарий адаптации: он владеет форматом задач. Он же переименует транслитные слаги в английские и тем же проходом починит перекрёстные ссылки;
  5. починка ссылок на перенесённое во всём репозитории — docs/, openspec/, CLAUDE.md, README.md;
  6. удаление оригиналов — только тех, чьё содержимое найдено в новом доме;
  7. шаг docs.py check в гейт проекта. Путь к скрипту — переменной с умолчанием на канонический путь маркетплейса, чтобы переустановка плагина не меняла Taskfile; шаг обязан краснеть внятно, если скрипт не найден, а не пропускаться. Передай ему базу диффа (--base) той же переменной, что и остальным шагам гейта: без неё сверка миграций со схемой не гоняется вовсе. Пример строки покажи человеку — гейт принадлежит проекту, и правит его он;
  8. 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 — канон вырос

  1. docs.py version — версия проекта и версия скрипта.
  2. Проект новее скрипта — обнови маркетплейс, а не проект: это отстал плагин.
  3. Иначе иди по changelog.md снизу вверх от версии проекта до текущей и делай названное в каждой записи. Записи независимы и применяются по порядку.
  4. Подними canon в docs/.pm.json до текущей.
  5. docs.py check.
  6. Позови обоих судейdoc-consistency и doc-code-drift.

Записи журнала описывают что сделать проекту. Если запись этого не говорит — это дефект журнала, и о нём надо сказать, а не догадываться.

Шаг 6 обязателен, и вот почему. check сверяет число в .pm.json с версией скрипта — и только его. Применена ли запись журнала по существу, он не знает: проект несёт "canon": 6 и может не иметь того, чего требовала любая из пройденных версий. Записи применяются руками (переименовать секцию, проставить типы, дописать раздел каждому fix), а ручной проход по нескольким записям подряд — ровно то место, где половина шага делается и забывается. Судьи и есть проверка, которой у upgrade иначе нет: doc-consistency увидит, что документы разошлись после переименований, doc-code-drift — что переехавший факт разошёлся с кодом.

Чего этот скилл не делает

  • Не сочиняет содержание. Пустой слот получает честную строку о том, что его наполнить пока нечем, а не выдуманный абзац. Придуманный периметр модели угроз хуже отсутствующего: по нему будут строиться находки.
  • Не удаляет то, чьё содержимое не нашло дом. Оригинал живёт, пока не названо поимённо, куда переехал каждый его кусок.
  • Не ведёт содержимое канона — это скилл docs. Здесь только раскладка.
  • Не заводит проект с нуля — это скилл init.
  • Не правит историю. В старых коммитах старые пути остаются, и это нормально.

Доклад

  • Что нашёл docs.py: код выхода и число пунктов дрейфа.
  • Что перенесено: файл → дом, числом и поимённо для спорного.
  • Удалённые дубли — с указанием, против какой спеки сверялся каждый.
  • Не разложилось — поимённо, с причиной.
  • Переходное состояние числами: честных строк, маркеров долга, задач без критериев.
  • Граница покрытия: что проверила машина, что судил ты, чего не смотрел никто.