Files
dev-skills/av-dev/skills/doc-canon/references/skeletons.md
T
av de12a4d8a3 слияние: три плагина стали одним av-dev, скиллы получили префиксы
Каталоги, агенты и общие дома переехали в av-dev/; скиллы названы по прежнему
плагину — doc-*, task-*, code-*, с двумя смысловыми именами вместо тавтологии:
doc-sync вместо docs, task-track вместо tasks. Манифесты сведены к двум
плагинам. Пространства имён вызовов и пути внутри дерева переписаны машинно;
проза, которая называет прежние плагины отдельными, идёт следующим шагом.
2026-08-13 10:10:51 +03:00

24 KiB
Raw Blame History

Скелеты документов канона

Что кладут init и canon adopt в незаполненный слот. Правило одно: честная информативная строка вместо заглушки. Проход читает строку как факт; <!-- заполнить: … --> он читает как пробел, и docs.py check о таком плейсхолдере напоминает.

Плейсхолдер ставится только там, где ответ обязан быть и его не спросили. Всё, чего в проекте пока просто нет, описывается словами, а не плейсхолдером.

Шаблоны — единственное место, где правило канона копируется намеренно. adr/README.md и review.md уезжают в репозиторий проекта и обязаны там что-то говорить; определение при этом остаётся в canon.md. Отсюда обязанность: правка такого правила в каноне тянет запись в changelog.md — и запись называет, какой файл проекта поднимает upgrade. Без этого копия в проекте останется на старой версии молча.

Каждая такая копия помечена и сверяется машиной. Дом обрамляется <!-- дом: <id> --><!-- /дом: <id> -->, копия — <!-- копия: <id> из <путь> --><!-- /копия: <id> -->; scripts/copies.py маркетплейса требует дословного совпадения. Правишь текст внутри маркеров — правь дом, а не копию.

Сама пара маркеров в проект не переносится. Это машинерия маркетплейса: путь в ней ведёт в дерево плагина, и в репозитории проекта он не разрешится ни во что. Кладя скелет, копируй содержимое между маркерами, а строки <!-- копия: … --> и <!-- /копия: … --> оставляй здесь.

docs/passport.md

# Паспорт проекта

Зачем это и для кого. [architecture.md](architecture.md) отвечает «как
устроено», [tasks/ROADMAP.md](../tasks/ROADMAP.md) — «в каком порядке», паспорт —
«зачем и для кого».

## Цель

<!-- заполнить: одна фраза без технических деталей -->

**Потребители** — список закрытый: он определяет, что считать нужным, а что
интересным.

| Кто | Что ему нужно от нас |
| --- | --- |

Цель достигнута, когда:

## Что целью не является

Граница домена. По ней в теме `architecture` судят, не перенесено ли понятие
через границу.

## Типовые сценарии

## Референсы

Где смотреть prior art, когда упёрлись.

docs/architecture.md

# Архитектура

Обзор: как сложено и где что работает. **Поведение системы здесь не
описывается** — его нормативный дом `openspec/specs/`.

## Принципы

## Компоненты

Каждый — строкой со ссылкой на capability, а не пересказом её требований.

## Внешние границы и форматы

## Эксплуатация

- Где работает, что рядом, кто перезапускает:
- Внешние зависимости поимённо и чем каждая отказывает (падает, отвечает
  медленно, молчит, отдаёт мусор):
- Кто заметит отказ и когда:
- Характер потока (непрерывный, по запросу, по расписанию):

## Единые точки проекта

Где генерируются идентификаторы и время; где единственный парсер входного
формата; где маппинг доменной ошибки в код ответа; где общий путь приёма.
Материал для вопроса «не появился ли второй способ делать то, что уже делается».

## Деплой

## Открытые вопросы

Пустой проект: «Архитектуры пока нет: кода нет. Наполняется первой задачей.» Нет внешних зависимостей: «Внешних зависимостей нет — смотри на диск и на СУБД.»

docs/database.md

# Схема хранилища

СУБД, миграции, правило времени и идентификаторов.

## Таблицы

## Представление данных

Чем физически лежит запись и что происходит при чтении и записи.

## Настройки с числовым значением

Таймаут занятости, режим журналирования, лимит тела, размер пула, ретеншен.
Без них замер не превращается в находку: пик памяти — аномалия только рядом
со строкой «запись лежит сжатой и распаковывается целиком».

Нет БД — файла нет, и в docs/.docs.json нет ключа migrations.

docs/security.md

# Модель угроз

## Периметр

<!-- заполнить: первой строкой, против кого защищаемся -->

Контур не развёрнут — назови оба периметра, целевой и сегодняшний, и скажи
прямо, против какого строятся находки.

## Недоверенный вход

Что приходит извне и каким каналом: тело запроса, файл, аргумент команды,
ответ внешней системы, содержимое архива.

## Из чего строятся пути и ключи

Раскладка файлов на диске, состав координатного ключа записи, имя каталога.
Отсюда строится выход за пределы песочницы.

## Что разграничивает доступ

## Что чувствительнее чего

## Что вне модели

Перечислить явно. Пустой пункт означает, что в теме `security` угрозу выдумают
за тебя, и находка никогда не будет исправлена.

docs/conventions/README.md

# Конвенции кода

Как мы пишем код — в отличие от `openspec/specs/`, который описывает, что
система делает.

**Прозой остаётся только то, что не выражается правилом.** Свойство, ставшее
правилом линтера, отсюда удаляется и переезжает в перечень ниже.

## Записи

## Механизировано

| Правило | Где механизировано |
| --- | --- |

Не названное здесь место механизации означает, что проход по конвенциям будет
добросовестно проверять уже проверенное.

Пустой проект: «Конвенций пока нет: код не написан. Наполняется по мере реального трения, а не вперёд.»

docs/research/README.md

# Разведка

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

**Каждый вывод — с числами и командой, которой получен**, чтобы его можно было
перепроверить.

## Как снималось

## Записи

Нет внешних источников: «Внешних источников данных нет — разведка неприменима.»

docs/adr/README.md

# Журнал решений

Одна запись — одно решение. **ADR продвигает уже написанное решение, а не
сочиняет его заново**: запись цитирует решение и ссылается на источник —
`openspec/changes/archive/<id>/design.md`, а у решения, принятого разведкой без
изменения, на её записку.

## Когда заводить

Верно одно из трёх:

<!-- копия: adr-когда-заводить из av-dev/skills/doc-canon/references/canon.md -->
- **дорогой откат** — переделка стоит дороже переписывания одного файла;
- **намеренный отказ** от очевидного подхода;
- **пересмотр прежнего решения** — тогда у старой записи обязателен статус
  «заменено на».
<!-- /копия: adr-когда-заводить -->

Не заводить для рутины и для того, что видно из кода и `git log`.

## Соглашения

- Имя файла — `ADR-ГГГГ-ММ-ДД-slug.md`, дата — когда решение реально принято.
  Слаг **английский по сути, а не транслитом**: `queue-as-table`, не
  `ochered-tablicej`. Форму имени и слаг проверяет `docs.py check`.
- Записи неизменяемы: передумали — новая запись, старой ставится статус.
- Активная запись статуса не имеет. Значений два: `заменено на ADR-…` и
  `устарело`; ставятся полем меты записи — `- **Статус:** …` рядом с датой и
  источником, а не абзацем в теле.

## Записи

Новые сверху.

| Дата | Запись | Статус |
| --- | --- | --- |

docs/adr/template.md

# Краткий заголовок решения

- **Дата:** ГГГГ-ММ-ДД
- **Источник:** openspec/changes/archive/<id>/design.md — либо записка разведки,
  если решение принято без изменения

Статус ставится тем же полем и только при пересмотре:
`- **Статус:** заменено на ADR-…` либо `- **Статус:** устарело`.
У активной записи поля нет.

## Решение

Что именно решено — одной фразой.

## Почему

Намерение и причина. Цитата из источника, а не пересказ. Пиши так, чтобы через
год было понятно без чтения переписки.

## Последствия

- `+` что стало лучше.
- `` чем платим: ограничения, риски, нагрузка на поддержку.

docs/review.md

# Ревью: настройка и журнал

## Как настроен конвейер

### Типовые узлы

Рода узлов проекта и 3–5 проверяемых свойств к каждому. Рода, а не инвентарь
пакетов: род, который проект задумал, но ещё не написал, включать полезно.

### Типовые ложноположительные

Находки, которые здесь выглядят убедительно и всегда неверны. Каждая — с одной
строкой «почему здесь это не дефект».

### Вопросы по темам

Форма: `<тема>: <вопрос> (<провенанс>)`. Главный источник — журнал ниже. Вопрос
задаёт тот проход, который закрывает эту тему на текущем прогоне, дополнительно
к обязательным.

**Адресуй теме, а не имени прохода.** Проходы переезжают между метками и
упраздняются; вопрос, адресованный проходу, перестанет задаваться в тот день,
когда тот уедет в старшую метку, — и заметить это будет нечем. Тема переезд
переживает.

Темы ядра: `requirements`, `autotests`, `conventions`, `architecture`,
`security`, `operations`. Плюс любая своя — та, под которую проект завёл в
`docs/` **свой** документ. Документы категорий `источник` и `процессный` тем не
порождают, и адресовать вопрос `passport`, `database`, `adr`, `research` или
`review` нельзя — таких тем нет. Вопрос про границу домена адресуй
`architecture`, вопрос про хранилище и числа — `operations`.

### Триггеры метки

Проектная конкретизация правила выбора метки. **Списка три: по одному на
каждую ось вверх и один вниз** — поимённо, узлами или capability.

**Крупное здесь** — про объём: что трогает несколько узлов или слоёв, переносит
ответственность между ними, перекладывает существующий код в новую форму.

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

Любая из двух осей поднимает прогон до `large`, старшей метки: там `security`,
`operations` и `architecture` проверяют запуском, и там же единственные замеры.
Метка рассчитана на **510% задач**; если сюда попадает каждая третья, списки
написаны слишком широко.

**Мелкое здесь** — опускает до `small`. Ориентир по доле — до трети задач, и в
любом случае меньше, чем `medium`: перевес `small` значит, что рабочее умолчание
сместилось само. Помни отрицательный тест конвейера: что
после мерджа не откатывается обратной правкой (миграция, формат на диске,
публичный контракт, имя), — не `small`, каким бы маленьким ни был дифф.

Уточняет умолчания конвейера, не отменяет их; рабочее умолчание — `medium`.

### Недоступно проверке

Оба подраздела — **по темам**: «в теме `operations` не проверяется X» читается,
а «не проверяется X» через месяц не найдёт ни один проход.

**Не проверит ни один проход** — принципиальная граница; по факту промаха не
пересматривается.

**Перестали проверять сознательно** — что, когда и почему, со ссылкой на запись
журнала. Пересматривается **первым**, как только что-то проскочило.

Тему, у которой в проекте нет дома, сюда писать не надо: её называет план
каждого прогона, и это честнее разовой записи.

## Журнал дефектов

Запись на каждый воспроизведённый дефект, сразу, а не ретроспективно: со
временем теряется не факт, а то, почему дефект не поймали.

Форма:

<!-- копия: журнал-дефектов-форма из av-dev/skills/code-review/references/review-journal.md -->
## ГГГГ-ММ-ДД — <краткое последствие> [проскочил|пойман]

- **Где:** путь:строка либо «конвейер, а не код»
- **Симптом:** как обнаружилось, кем и когда
- **Причина:** что на самом деле было не так
- **Чем воспроизведён:** тест, команда, замер — с числами
- **Почему не поймали:** только для проскочивших — какой проход обязан был найти
  и что ему помешало
- **Что меняем:** правило прохода, шаг гейта, конвенция, факт в документе
  проекта — либо «ничего, цена поимки выше цены дефекта»
<!-- /копия: журнал-дефектов-форма -->

Пара маркеров копия: внутри — машинерия маркетплейса; в docs/review.md проекта уезжает только содержимое между ними (см. выше).

Новый проект: «Дефектов пока не было. Настройка конвейера появится с первым ревью.»

CLAUDE.md

Лежит в корне, не в docs/. Единственный файл канона, который агент читает всегда, поэтому в нём то, без чего нельзя сделать ни шага.

# CLAUDE.md

Памятка для работы над <проект>. Перед задачей прочитай также
[docs/passport.md](docs/passport.md), [docs/architecture.md](docs/architecture.md)
и [docs/conventions/](docs/conventions/README.md).

## Что это

Абзац: что делает и чего **не** делает.

## Стек

## Инварианты

Что нарушать нельзя. Каждый пункт — три вещи: формулировка **как проверяемое
свойство**, а не лозунг; последствие нарушения и его обратимость; **severity**
рядом. По этим формулировкам проходы ревью присваивают `critical`, поэтому
severity стоит здесь, а не выводится каждым проходом заново.

## Команды

## Гейт

- Команда целиком и как определяется база диффа:
- Где логи шагов:
- Что означает каждый исход:
- **Что красит безусловно и почему:**
- Чего в гейте намеренно нет и **кто тогда обязан это гонять:**

## Запреты

Что запускать нельзя, **с путями**: рабочая БД, боевой каталог данных, внешние
сервисы. Плюс где `testdata` и куда писать временное.

## Работа

- **Основная ветка:** <имя>
- **Необратимое** (спрашивается у человека всегда):
- **Что считается сломанным** — какая красная проверка обгоняет развитие,
  то есть останавливает текущую работу:
- **Ориентир по размеру порции:** своё число, если замерялось
- **Что такое «сделана»:** конвейер проекта пройден + критерии приёмки проверены
  поимённо

## Язык

- Документация, комментарии, сообщения коммитов — русский.
- Код и идентификаторы — английский.

Имя основной ветки, запреты с путями и «что необратимо» — не украшение: без первого падают git-операции батча и расчёт базы диффа, без второго проход может тронуть рабочие данные, без третьего вся шкала ранжирования триажа держится на догадке.

openspec/config.yaml

Образец переехал. Файл заводит и заполняет плагин конвейера — скилл av-dev:code-openspec, — потому что по OpenSpec работает он, а не канон документов. Проект без конвейера каталога openspec/ не имеет вовсе, и образец файла, которого у него нет, в скелетах канона лежал бы мёртвым грузом.

Форму не проверяет и docs.py — с канона 10 он о файле молчит вовсе. Проверяет её тот же владелец: скилл av-dev:code-openspec, команда openspec.py check. Плагина конвейера в проекте может не быть — тогда форму не смотрит никто, и это строка доклада, а не поломка. Что канон о файле всё же говорит (единственный дом, а не форма) — canon.md, раздел openspec/config.yaml.

docs/.docs.json

{
  "canon": <текущая версия>
}

<текущая версия> подставляет init или adopt, целым числом; берётся она из docs.py version (строка «канон скрипта»), а не из памяти. Литерал здесь протухает при каждом повышении канона, поэтому его тут и нет: незамещённый плейсхолдер ломает разбор JSON громко, а отставшее число дало бы дрейф молча.

Плюс "migrations": "<путь>", если есть БД. Ключа "tasks" здесь нет: настройки каталога задач и версия их формата переехали в свой файл <каталог задач>/.tasks.json, потому что ведёт их другой плагин. Состав ключей — canon.md.

Имя файла — по плагину-владельцу, av-dev-docs. До версии 13 он звался .pm.json, по распавшемуся av-dev-pm; проект с прежним именем docs.py check называет отдельной строкой и зовёт переименовать.