скиллы: doc-canon стал canon, версия раскладки поднята до 2
- каталог скилла и все вызовы переименованы: префикс `doc-` называл материал, а скилл занят формой — раскладкой всех частей проекта и общим повышением версии, включая каталог задач; - README перестроен: `canon` вынесен из семейства документов отдельным блоком и отдельным узлом графа, правило префиксов переформулировано, у документов уточнено владение — содержимым, а не раскладкой; - заведена запись 2 журнала версий: в проекте ничего не переехало, но путь к `docs.py` и имя вызова живут в гейте и в `CLAUDE.md` проекта и сломаются молча; - прежние адреса в записи 1 и в журнале решений оставлены как есть: журнал описывает состояния, которые были, и задним числом не переписывается.
This commit is contained in:
@@ -0,0 +1,356 @@
|
||||
---
|
||||
name: canon
|
||||
description: Форма раскладки проекта под 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/canon.md).** Здесь оно не
|
||||
пересказывается: два описания одной раскладки разъедутся, и работать будет то,
|
||||
которое прочитали последним. Прочитай его **до** первой правки.
|
||||
|
||||
- [references/skeletons.md](references/skeletons.md) — **что именно класть** в
|
||||
каждый незаполненный слот. Не выдумывай заглушку своей формы: `docs.py`
|
||||
узнаёт только плейсхолдер `<!-- заполнить: … -->` из шаблонов.
|
||||
- [shared/language.md](../../shared/language.md) — **как это написано словами**:
|
||||
информационный стиль, применённый к проектным текстам, таблицы англицизмов и
|
||||
жаргона. Раскладка отвечает, где текст лежит, — этот файл отвечает, каким он
|
||||
должен быть. Правила общие для документов канона, задач, решений ADR и
|
||||
записок разведки, и это их **дом**. Вычитывают их два прохода по охвату:
|
||||
документы — `doc-wording`, записи каталога задач — `task-wording`.
|
||||
- [references/changelog.md](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 <корень> # версия раскладки: скрипта и проекта
|
||||
python3 $ds bump --dir <корень> # поднять версию проекта до версии скрипта
|
||||
```
|
||||
|
||||
**Формы `openspec/config.yaml` здесь больше нет.** Каталог принадлежит конвейеру,
|
||||
и форму смотрит его скрипт — скилл `av-dev:code-openspec`, команда
|
||||
`openspec.py check`. Проект работает по OpenSpec, а каталога `openspec/` нет — форму
|
||||
не проверяет никто, и это надо сказать строкой доклада, а не считать, что она
|
||||
верна.
|
||||
|
||||
**Копия.** Дом словаря — `shared/axes.md` в репозитории плагина: словарь общий
|
||||
для всех скриптов, и ни один скилл им не владеет. Правится дом, а не этот файл.
|
||||
|
||||
<!-- копия: коды-выхода из av-dev/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/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`
|
||||
|
||||
1. `docs.py check`, при наличии базы диффа — с `--base`.
|
||||
2. **Судей документов на каждом `check` не зови.** Ими владеет отдельный скилл —
|
||||
`av-dev:doc-healthcheck`, — и там же записано, когда его звать: он дорог, и
|
||||
прогон по каждому `check` не окупается. `check` отвечает на «сходится ли
|
||||
форма», `doc-healthcheck` — на «не разошлись ли утверждения».
|
||||
3. Доклад: вывод скрипта строкой исхода и **граница покрытия** — что смотрели и
|
||||
чего не смотрели. Если суждение здесь нужно, скажи это строкой и предложи
|
||||
`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. Перенеси
|
||||
|
||||
Порядок важен — он минимизирует окно, в котором ссылки битые:
|
||||
|
||||
1. `.av-dev.toml` в корне: `version = <текущая версия>` и путь миграций в
|
||||
`[docs]`, если БД есть;
|
||||
2. каталоги канона и скелет **по [references/skeletons.md](references/skeletons.md)**:
|
||||
незаполненное — одной честной информативной строкой, а не «TBD»;
|
||||
3. **OpenSpec, если его нет или `config.yaml` остался примером** — **вызови
|
||||
Skill `av-dev:code-openspec`**. Каталог принадлежит конвейеру, и команда
|
||||
заведения с формой файла живут там. Пересказ инвариантов, конвенций и правил
|
||||
ревью из `context` вычисти ссылкой на дом — на переводимом проекте он там
|
||||
почти наверняка есть. Проект решил жить без OpenSpec — `docs.py` о каталоге
|
||||
тогда тоже молчит, и форму `config.yaml` не проверяет никто; скажи это
|
||||
строкой;
|
||||
4. переносы содержимого;
|
||||
5. каталог задач — **вызови скилл `av-dev:task-track`**, сценарий адаптации: он
|
||||
владеет форматом задач. Он же переименует транслитные слаги в английские и
|
||||
тем же проходом починит перекрёстные ссылки;
|
||||
6. починка ссылок на перенесённое во всём репозитории — `docs/`, `openspec/`,
|
||||
`CLAUDE.md`, `README.md`;
|
||||
7. удаление оригиналов — **только тех, чьё содержимое найдено в новом доме**;
|
||||
8. **шаг `docs.py check` в гейт проекта.** Путь к скрипту — переменной с
|
||||
умолчанием на канонический путь маркетплейса, чтобы переустановка плагина не
|
||||
меняла `Taskfile`; шаг обязан **краснеть внятно**, если скрипт не найден, а не
|
||||
пропускаться. Передай ему базу диффа (`--base`) той же переменной, что и
|
||||
остальным шагам гейта: без неё сверка миграций со схемой не гоняется вовсе.
|
||||
Пример строки покажи человеку — гейт принадлежит проекту, и правит его он.
|
||||
|
||||
**Шагов в гейте три, и они независимы.** `docs.py check` не тянет за собой
|
||||
ни задачи, ни конвейер: без своих строк дрейф каталога задач и формы
|
||||
`openspec/config.yaml` перестаёт ловиться совсем. Ставь соседние шаги по
|
||||
следу присутствия — каталог задач с индексом на месте, значит ставится
|
||||
`tasks.py check --dir <каталог задач>`; `openspec/config.yaml` есть, значит
|
||||
ставится `openspec.py check`. Следа нет — этой части в проекте нет, шаг не
|
||||
ставится, и это **строка доклада**, а не поломка: назови, чего теперь не
|
||||
проверяет никто. У каждого шага своя переменная пути с тем же умолчанием на
|
||||
канонический путь маркетплейса; `$CLAUDE_PLUGIN_ROOT` в гейт не подставляй —
|
||||
он ведёт только в свой плагин;
|
||||
9. `docs.py check` — до **отсутствия дрейфа раскладки**. Замечания
|
||||
(незаполненные плейсхолдеры, слабое упоминание capability) остаются:
|
||||
незаполненный канон это объявленное переходное состояние из шага 5, а не
|
||||
отказ. Пересчитай эти пункты в докладе переходного состояния — не выдавай
|
||||
их за поломку и не молчи о них.
|
||||
|
||||
**Задачи `docs.py` не проверяет** — их ведёт другой скилл, и согласованность
|
||||
каталога показывает только `tasks.py check`. Позвал на шаге 5 скилл задач —
|
||||
его отчёт идёт в доклад отдельной строкой, и пункт «задачи без цели» в нём
|
||||
зелёным не станет: цели не сочиняются адаптацией (запрет записан у того, кто
|
||||
ведёт задачи), их проставляет человек порциями переоценки на первом груминге —
|
||||
скилл `av-dev:task-groom`.
|
||||
|
||||
### 5. Объяви переходное состояние
|
||||
|
||||
Сразу после переноса канон **заполнен не весь**, и это нормально, но обязано
|
||||
быть названо, иначе следующий агент примет скелет за поломку.
|
||||
|
||||
Печатается по факту: сколько документов стоят честной строкой вместо
|
||||
содержания, сколько маркеров долга в `architecture.md`, сколько задач без
|
||||
критериев приёмки. Закрывается порциями по ходу работы, а не одним заходом.
|
||||
|
||||
### 6. Позови обоих судей
|
||||
|
||||
`docs.py` увидел раскладку, а не смысл: перенос растащил один факт по двум домам,
|
||||
оставил в `architecture.md` поведение, которому место в спеке, и оторвал ADR от
|
||||
его `design.md`. Ничего из этого скрипт не видит, и первый прогон на живом
|
||||
проекте обычно самый урожайный — правило единственного дома до адаптации никто не
|
||||
проверял.
|
||||
|
||||
Вызови Skill **`av-dev:doc-healthcheck`** — он зовёт обоих судей на весь канон
|
||||
разом и держит разбор урожая порциями.
|
||||
|
||||
**Передай им объявленное переходное состояние из шага 5** — иначе честная строка
|
||||
в незаполненном слоте вернётся находкой, а это не поломка, а объявленный долг.
|
||||
|
||||
### 7. Вычитай написанное — агент `doc-wording`
|
||||
|
||||
Судьи смотрят утверждения, а `adopt` только что **писал текст**: честные строки
|
||||
в пустые слоты, переписанные при переносе абзацы, шапки перенесённых документов.
|
||||
Язык этого текста не проверяет никто другой, а зовущий здесь по определению тот,
|
||||
кто его и написал.
|
||||
|
||||
Позови агента **по названной пачке** — документы, которые ты завёл или правил,
|
||||
плюс перенесённые целиком. Весь канон ему не нужен: он работает по списку, и
|
||||
список же служит ему словарём терминов. Находки — готовые формулировки,
|
||||
подставляешь их ты.
|
||||
|
||||
## `upgrade` — канон вырос
|
||||
|
||||
1. `docs.py version` — версия проекта и версия скрипта.
|
||||
2. Проект новее скрипта — **обнови маркетплейс**, а не проект: это отстал
|
||||
плагин.
|
||||
3. Иначе иди по [changelog.md](references/changelog.md) снизу вверх от версии
|
||||
проекта до текущей и делай названное в каждой записи. Записи независимы и
|
||||
применяются по порядку.
|
||||
4. Подними версию — `docs.py bump`. Он правит **строку**, а не переписывает
|
||||
файл: комментарии в нём принадлежат проекту. Последним шагом, потому что
|
||||
число объявляет пройденными записи журнала.
|
||||
5. `docs.py check`.
|
||||
6. **Позови судей** — Skill `av-dev:doc-healthcheck`.
|
||||
7. **Позови вычитку** — агент `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`: код выхода и число пунктов дрейфа.
|
||||
- Что перенесено: файл → дом, числом и поимённо для спорного.
|
||||
- **Удалённые дубли** — с указанием, против какой спеки сверялся каждый.
|
||||
- **Не разложилось** — поимённо, с причиной.
|
||||
- Переходное состояние числами: честных строк, маркеров долга, задач без
|
||||
критериев.
|
||||
- **Граница покрытия**: что проверила машина, что судил ты, чего не смотрел
|
||||
никто.
|
||||
Reference in New Issue
Block a user