Files
dev-skills/av-dev-pm/skills/canon/references/skeletons.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

21 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 маркетплейса требует дословного совпадения. Комментарии невидимы в отрендеренном markdown и уезжают в проект вместе со скелетом — там они говорят читателю, что у текста есть дом. Правишь текст внутри маркеров — правь дом, а не копию.

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/.pm.json нет ключа migrations.

docs/security.md

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

## Периметр

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

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

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

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

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

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

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

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

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

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

docs/conventions/README.md

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

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

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

## Записи

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

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

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

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

docs/research/README.md

# Разведка

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

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

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

## Записи

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

docs/adr/README.md

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

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

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

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

<!-- копия: adr-когда-заводить из av-dev-pm/skills/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`. Помни отрицательный тест конвейера: что
после мерджа не откатывается обратной правкой (миграция, формат на диске,
публичный контракт, имя), — не `small`, каким бы маленьким ни был дифф.

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

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

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

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

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

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

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

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

Форма:

<!-- копия: журнал-дефектов-форма из av-dev-pipeline/skills/review-pipeline/references/review-journal.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` и куда писать временное.

## Работа

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

## Язык

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

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

docs/.pm.json

{
  "canon": 6
}

Плюс "migrations": "<путь>", если есть БД. Ключ "tasks" заводится только когда имя файла или заголовка отличается от умолчания ({"backlog": "INDEX.md"}); секций беклога в нём нет — их дом заголовки ## индекса. Состав ключей — canon.md.