Files
dev-skills/av-dev-code/skills/openspec/SKILL.md
T
av 863769406f канон 13: файл версии зовётся по владельцу, у задач появилась своя версия формата
Имя `.pm.json` пережило плагин `av-dev-pm` на два месяца и указывало в пустоту.
Правило, которое из этого вынуто: имя служебного файла — имя плагина, который
его завёл, и по нему же владельца узнают.

- `docs/.pm.json` → `docs/.docs.json`, запись 13 журнала. Прежнее имя docs.py
  не читает намеренно: по этому числу upgrade решает, какие записи применять,
  и два дома разъехались бы молча ровно там, где это дороже всего. Вместо
  совместимости — узнавание: check видит старый файл и печатает готовую git mv
- у каталога задач появилась своя версия формата — ключ `tasks` в
  `.tasks.json`, свой журнал версий и своё повышение. До сих пор её не было
  вовсе, хотя docs.py в комментарии уверенно на неё ссылался: описание
  опережало механику ровно так, как сказано в решении 195
- число своё, а не копия канонического: плагин ставится в одиночку, и у
  проекта без docs/ версии канона нет — сверять было бы не с чем
- конфиг задач стал обязательным (init и adopt apply пишут его всегда), check
  сверяет число, `check --fix` его не приписывает: приписанное объявляло бы
  каталог приведённым к формату, шагов которого никто не делал
- переезды 11 и 12 в новый журнал задним числом не переписаны — версия 1
  велит догнать формат по журналу канона, называя признаки отставания
  поимённо (каталог в docs/tasks/, живой SPRINT.md)
- запись 60 в DECISIONS со следствиями 200–203; отдельно разведено с решением
  F, где `.docs.json` отвергался как указатель путей: отвергнут был указатель,
  а не имя
2026-08-11 10:35:39 +03:00

14 KiB
Raw Blame History

name, description
name description
openspec Завести и настроить OpenSpec в проекте — openspec init --tools claude, замена закомментированного примера в openspec/config.yaml на настройку канонической формы (язык, правила именования capability, придирки валидатора, адреса паспорта и CLAUDE.md), проверка формы своим скриптом openspec.py (имя файла, схема, незаменённый пример, адреса документов, ключи rules против артефактов схемы) и сверка слепка с живой версией инструмента. Использовать, когда в проекте нет каталога openspec/, когда config.yaml остался примером из коробки, когда заводят новый проект или переводят чужой и дошли до шага OpenSpec, а также когда конвейер отказался работать без источника требований. Каталог openspec нужен именно конвейеру: без него не работают ни opsx:propose, ни ревью дизайна, ни сверка требований.

OpenSpec в проекте

Каталог openspec/предпосылка конвейера, а не канона документов. Без него не работают ни opsx:propose, ни ревью дизайна, ни review-specs: у требований не остаётся дома. Поэтому заводит и настраивает его этот плагин — тот, кто по OpenSpec и работает.

Канон документов о файле не высказывается вовсе: docs.py его не открывает и об его отсутствии молчит. Проект без конвейера живёт без OpenSpec законно, и проверять там нечего. За каноном остаётся одно — единственный дом: не пересказан ли в context документ, у которого есть свой файл. Это суждение, а не форма, и смотрит его агент.

Два шага, и второй важнее первого

1. Завести.

openspec init --tools claude

Команда кладёт ещё .claude/skills/openspec-* и .claude/commands/opsx/* — это её нормальная работа, не трогай их.

2. Заменить пример. openspec init кладёт config.yaml, где context и rules — закомментированный пример на английском. Файл из коробки хуже отсутствующего: он есть, он валиден, имя правильное, — и читается как настроенный, работая как пустой. Узнаётся это по уже написанному предложению: на другом языке, с capability по имени пакета, без единого SHALL.

Пример заменяется целиком по образцу: references/config-skeleton.md.

Что туда пишут, а что нет

Это маршрутизатор, а не второй дом фактов. Внутрь идёт ровно то, что нужно в момент порождения артефакта и чего в этот момент ещё никто не открыл: язык, правила именования capability, придирки валидатора и адреса документов проекта.

Сюда же — требования к форме proposal и design, на которых стоит чекпоинт скилла av-dev-code:resolve: объяснение человеку собирается из этих двух артефактов, и требование к ним обязано применяться в момент, когда их пишут, а не вспоминаться шагом позже. Образец их содержит.

Пересказ паспорта, инвариантов, конвенций и правил ревью сюда не переносится. Место для второго дома здесь самое частое: context читается при порождении каждого артефакта, туда удобно дописать «чтобы агент знал», и так заводятся копии инвариантов, состава гейта и правил выбора метки. Расходятся они молча, а замечают это в уже написанном предложении.

Разрез, по которому отличают одно от другого: утверждение, которое можно опровергнуть, открыв другой файл проекта, — пересказ; строка, которая говорит, какой файл открыть, — ссылка. Машина этот разрез не проверяет; его смотрит агент doc-consistency из плагина канона, когда тот подключён.

Два адреса обязательны — docs/passport.md и CLAUDE.md: предложение пишется до того, как кто-либо откроет docs/, и без них его пишут, не зная ни границы домена, ни инвариантов. Отсутствие адреса к существующему документу openspec.py check называет отказом; документа нет в проекте — нет и требования.

Инструмент

os="$CLAUDE_PLUGIN_ROOT/skills/openspec/scripts/openspec.py"

python3 $os check --dir <корень>   # форма config.yaml в проекте
python3 $os form                   # слепок формы против живого OpenSpec

Коды выхода — общий словарь скриптов av-dev: 0 сошлось, 1 дрейф, 2 ошибка употребления, 3 окружение, 4 внутренний сбой. Ветвись на коде, а не на тексте. Различать 1 и 3 обязательно: «форма разошлась» — рабочая ситуация, «openspec не отвечает» — нерабочая.

check проверяет форму, и каждая проверка — про молчащий пробел, а не про вкус: каталог есть; имя именно config.yaml (config.yml OpenSpec не читает и об этом не сообщает); ключ schema называет ту схему, для которой форма описана; context и rules.specs не остались примером, а SHALL назван именно внутри rules.specscontext он стоит и в образце, поэтому греп по файлу здесь ничего не значит); context называет паспорт и CLAUDE.md; ключи под rules: — имена артефактов схемы, а не свободные слова. Числа проверок здесь нет намеренно: оно протухает от каждой добавленной.

Адреса требуются только к тем документам, которые в проекте есть. Канон документов ставится отдельным плагином и может быть не подключён; требовать ссылку на несуществующий файл значит требовать битую ссылку. Нет docs/passport.md — проверка по нему идёт строкой «не проверялось», и там же сказано, что без канона конвейер работает вслепую.

Форма сверяется с живым инструментом

Схема (spec-driven) и перечень артефактов (proposal, specs, design, tasks) — состояние чужого инструмента, а не наше решение. OpenSpec переименует артефакт: правила под прежним именем перестанут применяться, конфиг останется выглядеть написанным, и молчат при этом все три стороны.

Сторож — сравнение версий. check каждым прогоном спрашивает openspec --version (десятые доли секунды) и сравнивает major.minor с той версией, на которой форма сверялась; разошлось — замечание, не отказ, с именем команды. Патч-версия в сравнение не берётся намеренно: формы она не меняет, а нагоняй на каждый багфикс приучает пролистывать весь блок.

Перепроверяет openspec.py form: он спрашивает openspec templates --json, то есть перечень артефактов текущей схемы, и печатает, что разошлось с константами. Дорогой вызов вынесен из check сознательно — он стоит втрое дороже опроса версии, а ответ меняется только вместе с версией. Чинится расхождение в плагине, а не в проекте: константы скрипта, образец references/config-skeleton.md и запись в журнал версий канона.

Кто зовёт этот скилл

  • av-dev-docs:init — шагом заведения нового проекта, до первого документа;
  • av-dev-docs:canon в режиме adopt — если на переводимом проекте каталога нет или config.yaml остался примером;
  • av-dev-code:resolve и av-dev-code:review — не вызовом по ходу, а отсылкой: OpenSpec у обоих жёсткая предпосылка, и на проекте без каталога оба посылают сюда вместо того, чтобы заводить его руками;
  • человек — когда конвейер отказался работать без источника требований.

Копия. Дом правила — shared/plugin-boundary.md в репозитории плагинов. Правится дом, а не этот файл.

Плагины av-dev ставятся порознь, и ни один не вправе считать, что сосед на месте.

Чужой скилл зовётся полным именемav-dev-docs:canon, av-dev-tasks:tasks, av-dev-code:review. Короткое имя может разрешиться в устаревшую проектную копию из .claude/skills/, и подмены не будет видно ни в докладе, ни в поведении.

Путь в дерево чужого плагина не пишется никогда. $CLAUDE_PLUGIN_ROOT ведёт только в свой плагин; вычисленный от него путь к соседу либо не откроется, либо откроет чужую установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его сам.

Вызов не разрешился — плагина в проекте нет. Это исход, а не поломка: назови строкой доклада, чего теперь не делает никто, и продолжай работу. Молчать нельзя, пропуск неотличим от сделанного; выдумывать обходной путь нельзя тоже.

Присутствие узнаётся вызовом или следом в проекте, но не объявлением. Перечня установленных плагинов проект не ведёт — он разошёлся бы с действительностью молча. Что сосед здесь работал, видно по заведённому им файлу: docs/.docs.json — канон, <каталог задач>/.tasks.json — задачи, openspec/config.yaml — конвейер. Имя файла — имя плагина, который его завёл, и держит он в том числе версию своего формата: у канона документов и у каталога задач они свои и двигаются порознь.

Здесь это значит: вызов не разрешился — плагина конвейера в проекте нет, и тогда OpenSpec заводит человек командой выше.

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

  • Не пишет спеки и предложения. Это opsx:propose и конвейер задачи.
  • Не ведёт документы канона — их дом плагин av-dev-docs, и адреса в context только на них ссылаются.
  • Не чинит расхождение формы с версией OpenSpec в проекте. Оно чинится в плагине: константы скрипта, образец здесь, запись в журнал версий канона.
  • Не судит, ссылается context на документы или пересказывает их. Машине этот разрез не виден; его смотрит агент doc-consistency из плагина канона. Плагина нет — эту проверку не делает никто, и так и скажи.