av-dev-pm расколот на av-dev-docs и av-dev-tasks

Плагин владел двумя разными вещами сразу — документацией проекта и учётом работ,
— и это мешало обеим. Канон нельзя было поставить без задач, задачи без канона, а
язык проектных текстов лежал внутри скилла canon и потому принадлежал половине.
Теперь плагина два, каждый ставится сам по себе.

av-dev-docs: скиллы canon, docs, init; агенты doc-consistency, doc-code-drift,
doc-wording; скрипт docs.py. av-dev-tasks: скиллы tasks, session; агенты
task-form, task-wording; скрипт tasks.py.

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

То, что нужно обоим дословно, стало вторым общим домом. Словарь «Сопровождение и
эксплуатация» назван в трёх местах трёх плагинов — секция роадмапа, раздел
«Эксплуатация» в architecture.md, тема ревью operations — и ни один из трёх им не
владеет; он уехал в shared/operations.md, а canon.md и скилл задач везут копии.
Три перечня «чем держат проект» уже разъезжались на «метриках и логах» против
«мониторинга», так что ссылка тут не годится: плагин, поставленный в одиночку,
получил бы указатель в никуда. Тем же способом язык: у av-dev-tasks появилась
своя копия language.md.

Копий стало 18 при 8 домах.

Переименования разведены по смыслу, а не заменой строки: где речь о каноне —
av-dev-docs, где об учёте задач — av-dev-tasks. В пайплайне таких мест
одиннадцать, и оба адресата там встречаются вперемешку.

Журналы (DECISIONS, TODO, HISTORY) намеренно не тронуты: они описывают состояние
на момент записи. По той же причине оставлена наблюдённая строка в комментарии
docs.py — она цитирует конфиг живого проекта, а не называет плагин.

Не входит в этот заход и названо отдельно: слияние canon и docs в один скилл,
разделение docs/.pm.json на два конфига и переезд openspec в пайплайн.

Гейт зелёный: копии, фронтматтеры, диаграммы, json. Оба скрипта прогнаны после
переезда — docs.py version и tasks.py check на фикстуре.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-08-09 14:06:26 +03:00
co-authored by Claude Opus 5
parent 86e22d932c
commit 00ddfb0dde
42 changed files with 417 additions and 97 deletions
+155
View File
@@ -0,0 +1,155 @@
---
name: docs
description: Вести содержимое документов канона по ходу разработки — синк после сделанной задачи с построчным отчётом по каждому документу, заведение ADR промоутом из архивного design.md, запись наблюдения в research, запись дефекта и настройки конвейера в review.md, чистка architecture.md от поведения с маркерами долга. Использовать, когда задача сделана и надо обновить документацию, когда просят завести ADR или записать решение, занести находку о внешних данных, записать проскочивший дефект, разгрузить разросшуюся архитектуру. Раскладку и соответствие канону проверяет скилл canon.
---
# Ведение содержимого канона
Скилл владеет **содержимым** документов канона; раскладкой владеет `canon`.
Определение канона и роли документов — [канон](../canon/references/canon.md),
здесь не пересказывается.
Главный вызывающий — **шаг синка документации в пайплайне задачи**. Пайплайн
живёт в другом плагине и зовёт этот скилл по имени; проект без пайплайна ведёт
документацию тем же скиллом вручную.
## Правило, из которого всё следует
**Принуждённое отрицание.** Синк обязан назвать **каждый** документ канона: либо
чем он обновлён, либо «не требуется, потому что…». Нетронутые группируются одной
строкой с общей причиной.
Причина, по которой правило именно такое, измерена: у ADR был список триггеров
прозой — и он дал **6 записей на 43 изменения**. Прозаический триггер, который
некому проверить, не срабатывает. Отличить «не написал» от «написал, что не
требуется» можно только тогда, когда отрицание обязательно.
Это тот же приём, что «границы покрытия» в отчёте ревью и «пустое называется
пустым» в каноне.
## Чек-лист синка
Идёт сверху вниз; каждая строка попадает в доклад.
| Документ | Обновляется, когда | Проверка |
| --- | --- | --- |
| `openspec/specs/` | всегда при изменении поведения | вливает `opsx:archive` |
| `database.md` | тронуты миграции | `docs.py check --base` |
| `architecture.md` | новый компонент, граница, внешняя зависимость, изменилось окружение | `docs.py`: capability без упоминания |
| `adr/` | дорогой откат, намеренный отказ, пересмотр прежнего | нет — только этот чек-лист |
| `research/` | узнали новое о внешнем формате или данных | нет |
| `security.md` | новый недоверенный вход, токен, путь наружу, сдвиг периметра | нет |
| `conventions/` | находка принята и не специфична для одного места | промоут |
| `review.md` | дефект воспроизведён; сузили или расширили проверку | нет |
| `passport.md` | новый потребитель, сдвиг границы «чем не является» | нет |
| `CLAUDE.md` | изменился инвариант, гейт, запрет, необратимое | нет |
Пример доклада:
```
Синк документации:
- architecture.md — добавлен воркер свёртки, ссылка на capability reindex
- database.md — миграция 00006, таблица bucket
- adr/ — заведён ADR-2026-08-03-queue-as-table: отказ от внешней очереди
- research/ — новое о формате не узнано
- passport, security, conventions, review — не требуется: изменение внутреннее
```
## Сверка — не здесь, а на сессии
Синк правит документы поодиночке, а расходятся они **между собой**: факт,
дописанный в `architecture.md`, уже живёт в `CLAUDE.md`; периметр в
`security.md` не знает про новый эндпоинт. Поймать это на своей же правке нельзя,
и судит это агент `doc-consistency`.
**Но синк его не зовёт.** Оба судьи документов — `doc-consistency` и
`doc-code-drift` — зовутся раз в спринт, шагом сессии, на весь канон разом.
Причина в цене: `doc-consistency` на `opus` по каждой сделанной задаче — самая
дорогая церемония процесса, а `doc-code-drift` хоть и на `sonnet`, но читает
репозиторий целиком. К тому же расхождение между двумя документами по определению
требует двух документов, а на большинстве задач синк правит один.
Что теряется: привязка находки к задаче, которая её породила. Что выигрывается,
кроме денег: синк перестаёт отбирать пачку, и в неё попадают документы,
которых работа не касалась, — расхождение, внесённое правкой в одном месте, там
и живёт.
## ADR — промоут, а не второе сочинение
Обоснование уже написано: `opsx:propose` кладёт `design.md` в каждый change, и
после архивации он лежит в `openspec/changes/archive/<id>/design.md` с разделами
`Context` / `Goals / Non-Goals` / `Decisions` / `Risks / Trade-offs`.
**ADR цитирует решение оттуда и ссылается на источник.** Не пересказывает и не
сочиняет заново.
**Триггер заведения, форма имени и правило замены — в
[каноне](../canon/references/canon.md), раздел `adr/`.** Здесь они не
повторяются: копия правила расходится с оригиналом на первой же смене версии
канона, а расходится незаметно.
Твоя часть — **применить триггер к этой задаче и сказать вслух, сработал он или
нет**. Строка «adr/ — не требуется: решение рутинное» и есть то, ради чего
чек-лист существует; её отсутствие неотличимо от «забыл посмотреть».
Порядок работы: открой архивный `design.md` change, найди в `Decisions` то, что
проходит триггер, процитируй решение и его причину, сошлись на источник, добавь
строку в индекс `docs/adr/README.md` сверху.
## Чистка `architecture.md`
Обзор не держит поведение — его нормативный дом `openspec/specs/`. **Форма
маркера долга и правило «гейт от них не краснеет» — в
[каноне](../canon/references/canon.md), раздел `architecture.md`.**
Разбирается порциями: раздел вычищает та задача, которая его касается.
Содержимое не выбрасывается, а переезжает — требования в дельта-спеку change,
обоснование в ADR, обзор остаётся строкой со ссылкой на capability.
## Запись в `research/`
Наблюдение о внешнем мире: что реально шлёт источник, чем документация формата
расходится с практикой. **Требование провенанса и правило про расходящееся
число — в [каноне](../canon/references/canon.md), раздел `research/`.**
Твоя часть — заметить, что по ходу задачи узналось новое о внешних данных, и не
дать этому остаться в контексте. Признак: ты правил разбор, опираясь на то, чего
нет ни в одном документе.
## Запись в `review.md`
Файл держит два раздела с разными сроками жизни — журнал дефектов и настройку
конвейера. **Что в каком и в какой форме — в
[каноне](../canon/references/canon.md), раздел `review.md`**; подробности формы
записи и выбор адреса, куда она ведёт, — у конвейера ревью проекта (при
`av-dev-pipeline``Skill av-dev-pipeline:review-pipeline`, его
`references/review-journal.md`). **Конвейера в проекте нет** — пиши по форме из
скелета `review.md`, которую положил канон, и скажи в докладе, что подробностей
формы взять негде.
Твоя часть на синке: **дефект пишется сразу**, а не «потом, когда починим».
Со временем теряется не факт, а то, почему дефект не поймали, — единственное,
ради чего журнал есть. И решение сузить проверки (перестали звать проход, понизили
метка) обязано попасть в раздел настройки, а не остаться в отчёте ревью.
## Промоут в конвенции
Находка → конвенция → правило линтера → **удаление из прозы**. Процедура целиком
принадлежит конвейеру ревью проекта (при `av-dev-pipeline` — его
`references/promote.md`, читается через `Skill av-dev-pipeline:review-pipeline`);
роль каталога конвенций — в [каноне](../canon/references/canon.md). **Конвейера
нет** — три шага всё равно твои, просто без его процедуры: сформулируй правило,
поищи, чем оно механизируется, и вычеркни прозу, если механизировалось.
Твоя часть — **третий шаг, который пропускают чаще всего**: правило заработало,
а формулировка осталась в прозе, и проход продолжает проверять уже проверенное.
На синке это отдельная строка: «conventions/ — правило X механизировано,
формулировка удалена» либо «не требуется».
## Чего этот скилл не делает
- **Не проверяет раскладку** — это `canon`.
- **Не заводит недостающие документы** — их скелет кладёт `canon adopt` или
`init`.
- **Не сочиняет содержание.** Нечего записать — так и пишется, честной строкой.
- **Не переоформляет документы «заодно»**: правится то, чего коснулась работа.