ревью по темам: документ проекта стал направлением проверки

Замечено при сверке документов канона с составом ступеней: три документа
остались без читателя ниже wide — security.md, database.md и adr/. Проект
поддерживал их, а на 90% задач не открывал никто. Причина оказалась не в
переезде проходов, а в том, как описан состав прогона.

Список тем нигде не был записан: он существовал побочным продуктом списка
проходов. Проход уезжал в верхнюю ступень — и тема уезжала с ним беззвучно.
Отчёт честно говорил «ops не запускался» и не говорил «эксплуатацию не смотрел
никто», а нужно второе. Теперь тема первична, проход вторичен — это правило 0
конвейера, а прогон описывается таблицей «тема → дом → глубина → кто закрывает»,
и таблица есть в каждом отчёте.

Тема есть документ, список открытый. Всё, что проект кладёт в docs/, становится
темой ревью; запретить нельзя, разрешения не надо. Не темы ровно две: docs/tasks/
и docs/review — настройка самого конвейера, слой над темами. Отсюда главное:
docs/ перестал быть документацией и стал конфигурацией конвейера. Проект
настраивает проверку тем, что пишет о себе, а не отдельным файлом настроек,
который разошёлся бы с документами. Ядро — requirements, autotests, conventions,
architecture, security, operations; всё сверх разбирает basics, потому что
именных проходов конечное число, а тем столько, сколько заведёт проект.

Тема живёт файлом или каталогом, на выбор проекта: docs/security.md и
docs/security/ — одно и то же. Прежде форма была задана поимённо и обосновать её
было нечем; заодно в TODO висел вопрос «а если architecture.md разрастётся».
Теперь ответ механический: разросся — стал каталогом с README.md, и это не смена
версии. Обе формы сразу — ошибка, docs.py её ловит.

Заведён review-scope, sonnet, стадия 0, до гейта: находит документы, выводит
темы, назначает глубины, выбирает ступень с обоснованием. Довод оказался сильнее
синхронизации документов — до сих пор профиль называл тот же оркестратор,
который написал код, то есть в точке выбора глубины проверки разведённости с
автором не было вовсе, а решала она под давлением «я почти закончил». Вызывающий
пайплайн профиль больше не передаёт. Поднять и понизить ступень разметчик вправе
одинаково, но обоснование обязательно всегда.

Sonnet ему хватает потому, что вывод устроен как список: каждый файл в docs/
обязан попасть в план темой или строкой «не тема, потому что», и план сверяется с
ls docs/ за секунду. Выбор ступени — суждение, но у него три независимых
корректора: отрицательный тест quick, правило «спорный случай вниз» и сигнал
basics о заниженной ступени.

Разметчик передаёт адреса, а не пересказ. Проект однажды уже держал
review-brief.md и убрал его: второй дом расходится с первым и выглядит
актуальным. Пересказ в задании — тот же посредник, живущий один прогон.
Исключение одно: отсутствие дома, этого проход сам дёшево не выяснит.

quick и standard совпали составом и разошлись глубиной — иначе требование
«нижние ступени закрывают все темы, просто не так глубоко» не выполняется.
Глубин три, и они про способ доказательства, а не про старательность: сверка
(открыть дом, открыть дифф, сравнить), разбор (построить сценарий рассуждением),
доказательство (прогнать, померить, построить путь). Третья есть только в wide.
Цена принята: это единственное место, где профиль не выводится из списка
проходов, поэтому глубина объявляется в отчёте наравне со ступенью.

review-code переписан, и это оказалось крупнее исходной находки: код как код не
читал никто. specs сверял с требованиями, basics — с отказами окружения,
architecture — с устройством, а code был проходом только по прозаическим
конвенциям и прямо объявлял, что рантайм и логика не его. «Здесь ошибка в логике»
не говорил вообще никто. Теперь у прохода две половины: девять классов
технического дефекта (необработанная ветка отказа, пустое и нулевое, граница
диапазона, перепутанный операнд, неосвобождённый ресурс, изменение под итерацией,
неверно применённый интерфейс библиотеки, недостижимая ветка, «сделано соседнее»)
и прежняя сверка с конвенциями. Модель поднята до opus по признаку темы 35: цена
пропущенной находки — дефект в проде.

Канон повышен до версии 5: форма дома на выбор, открытый список тем, AGENTS.md
законно лежит рядом с CLAUDE.md, «Вопросы к проходам» → «Вопросы по темам» (имя
прохода переезд не переживает, тема переживает), «Недоступно проверке» — тоже по
темам. docs.py переписан под темы: ловит двойной дом, принимает обе формы,
перечисляет свои темы проекта вместо «файл вне канона».

Побочно закрыт давний пункт TODO про каталожную форму architecture.md — решать
больше нечего.

Прогон от всего этого стал дороже, а не дешевле, впервые за сессию: плюс scope в
голове каждого прогона, плюс code на opus, плюс basics теперь и в quick. Куплены
разведённость выбора ступени, видимость непокрытых тем и технический разбор кода,
которого не было вовсе.

Тема 36 в DECISIONS.md, следствия 137-140.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-08-07 08:35:11 +03:00
co-authored by Claude Opus 5
parent c93a9d1269
commit a81dd1a5a7
17 changed files with 1356 additions and 615 deletions
+283 -154
View File
@@ -1,6 +1,6 @@
---
name: review-pipeline
description: "Конвейер ревью изменения — детерминированный гейт, сверка с дельта-спеками в обе стороны, базовый проход на отказы и лишнее, а в верхнем профиле враждебные постановки, эксплуатационный постмортем и архитектурный проход; триаж обязателен всегда. Три ступени стоимости: quick (4 прохода), standard (5, рабочее умолчание), wide (7, только крупное или незнакомое — 5-10% задач). Ступень выбирается по объёму и незнакомости изменения, спорный случай решается вниз. Порядок прогона — граф зависимостей, а не очередь: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Линейный прогон — по слову оператора или на занятой машине. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода."
description: "Конвейер ревью изменения, устроенный по темам: каждый документ проекта — тема ревью, а проход лишь закрывает тему на заданной глубине. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список открытый, свои темы проект заводит документом. Прогон начинает разметчик: находит документы, выводит темы, выбирает ступень с обоснованием и раздаёт темы проходам. Три ступени: quick и standard закрывают все темы (сверкой и разбором), wide добавляет доказательство — враждебные постановки, эксплуатационный постмортем, архитектурный проход на широком входе. Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода."
---
# Конвейер ревью
@@ -8,10 +8,19 @@ description: "Конвейер ревью изменения — детерми
Готовит ревью — **не заменяет его**. Потребитель отчёта — оркестратор, который
чинит код; человек читает только сводку, развилки и границы покрытия.
## Три правила, из которых всё следует
## Четыре правила, из которых всё следует
Если ситуация не покрыта инструкцией — решай по ним.
0. **Тема первична, проход вторичен.** Ревью проверяет **темы** — набор
направлений, который проект объявляет своими документами. Проход это только
способ закрыть тему на заданной глубине, и проходы меняются: уезжают в верхнюю
ступень, сливаются, упраздняются. Если состав прогона считать списком проходов,
то уехавший проход уносит тему с собой **беззвучно** — отчёт честно скажет
«`ops` не запускался» и не скажет «эксплуатацию не смотрел никто», а нужно
второе. Поэтому прогон описывается таблицей «тема → глубина → кто закрывает», и
таблица эта есть в каждом отчёте.
1. **Recall чек-листа равен длине чек-листа.** Проход, устроенный как «проверь
пункты 1..N», найдёт ровно перечисленное. Всё неявное — идиомы, форма
решения, «так не делают» — неперечислимо по определению: перечислимое уже
@@ -54,24 +63,45 @@ description: "Конвейер ревью изменения — детерми
`av-dev-pipeline:review-pipeline`, `av-dev-pipeline:task-pipeline`,
`av-dev-pipeline:task-batch`.
## Что конвейер защищает — приходит из документов проекта
## Темы — и почему их список открытый
Проходы общие, а нарушать нельзя проектное. Инварианты, команду гейта, объёмы,
прецеденты и модель угроз конвейер **не знает** — он читает их в документах
канона `av-dev-pm`, **напрямую и по жёстким путям**. Отдельного файла-брифа нет:
пути известны, посредник не нужен, а второй дом для тех же фактов разошёлся бы и
выглядел актуальным.
**Каждый документ проекта — тема ревью.** Форма дома значения не имеет:
`docs/security.md` и `docs/security/` — одна тема `security`, проект выбирает
форму по объёму написанного. Завёл документ — завёл тему; запретить нельзя,
разрешения не надо.
Карта «что нужно проходу → где лежит» —
[references/project-facts.md](references/project-facts.md). Прочитай её до
раздачи заданий; там же таблица поразрядной деградации.
Из этого следует то, ради чего правило и заведено: **`docs/` перестаёт быть просто
документацией и становится конфигурацией конвейера**. Проект настраивает ревью
тем, что пишет о себе, а не отдельным файлом настроек, который разошёлся бы с
документами.
**Деградация поразрядная, а не всё-или-ничего.** Документа нет — деградирует то,
что из него читалось, и только оно: нет `docs/security.md` — слабеет
`adversary`; нет `docs/research/` — числа неизвестны трём проходам; нет
инвариантов в `CLAUDE.md``critical` по основанию «нарушен инвариант проекта»
не присваивается никем. Каждый проход пишет **свою** строку в границы покрытия, с
**причиной**; триаж сводит их и не сливает в одну.
Ядро — шесть тем, они есть у любого проекта, приведённого к канону:
| Тема | Дом | Вопрос темы |
|---|---|---|
| `requirements` | `openspec/specs/`, дельты change | делает ли код заказанное, и только его |
| `autotests` | `CLAUDE.md`: семантика гейта, команды | проверено ли машиной и хватает ли проверок |
| `conventions` | `docs/conventions.*` | написано ли так, как здесь пишут |
| `architecture` | `docs/architecture.*`, `passport.*`, `adr/` | цело ли устройство: понятия, границы, решения |
| `security` | `docs/security.*` | что сделает недоверенный вход |
| `operations` | `docs/architecture.*`, `database.*`, `research/` | что будет через неделю на проде |
Не темы — их ровно две: `docs/tasks/` (каталог задач, его ведёт
`av-dev-pm:tasks`) и `docs/review.*` (настройка самого конвейера и журнал
дефектов — слой **над** темами). Обе называются в плане явно, а не пропускаются
молча.
**Проектная тема закрывается `basics`**, на любой ступени. Именных проходов
конечное число, а тем — сколько заведёт проект; приёмник обязателен, иначе
открытость списка была бы обещанием без механизма.
**Тема без дома — законное состояние и отдельная строка.** «Тема `operations`
заявлена, `docs/database.md` нет» читается иначе, чем «не смотрели». Деградация
поразрядная: нет дома — падает глубина этой темы, и только её.
Что именно проход читает по каждой теме — [references/project-facts.md](references/project-facts.md).
Отдельного файла-брифа при этом нет: пути известны, посредник не нужен, а второй
дом для тех же фактов разошёлся бы и выглядел актуальным.
**Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой
и предложи скилл `av-dev-pm:canon`: одна операция на проект против деградации на
@@ -79,34 +109,41 @@ description: "Конвейер ревью изменения — детерми
## Что получает каждый проход
Задание любому проходу состоит из шести вещей:
Задание собирается **по плану разметчика** (стадия 0) и состоит из шести вещей:
- **его блок вопросов** из «Вопросы к проходам» в `docs/review.md`, если он там
есть, — **дословно**. Блок адресован проходу поимённо и выведен из промаха
этого проекта; заставлять девять charter'ов самим ходить за ним значит
получить, что за ним ходят двое. Проход отвечает на такие вопросы явно,
дополнительно к обязательным;
- **его темы** — какие темы он закрывает на этом прогоне, у каждой **дом**
(путь и раздел) и **глубина**. Дом передаётся адресом, а не пересказом: проход,
получивший проинтерпретированный периметр, не заметит, что интерпретация
неверна;
- **вопросы по его темам** из `docs/review.*`, если они там есть, — **дословно**.
Вопрос привязан к теме, а не к имени прохода, и потому переживает переезд
прохода между ступенями;
- **контракт находок** — путь к
[references/finding-contract.md](references/finding-contract.md) (в
установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/`);
- **изменение** — идентификатор change и путь к его дельта-спекам;
- **база диффа**;
- **профиль и режим** прогона — чтобы проход знал, что писать в границы покрытия;
- **сужение**, если оно есть: конкретный узел, конкретная capability.
- **профиль и режим** прогона — чтобы проход знал, что писать в границы покрытия.
Чего проход **не** получает ни в каком режиме — выводов других проходов. См.
«Порядок прогона».
## Модель по проходу
Следует из правила 2: чем больше работы делает детерминированный инструмент,
тем дешевле может быть модель; чем больше проход **порождает** критерий, тем
дороже. Модель задана во frontmatter каждого агента, менять её здесь не нужно.
Модель выбирается **по цене ошибки прохода, а не по его роду**. Признак рабочий и
проверяемый: находка со ссылкой на записанный источник — строку спеки, цель в
манифесте, значение в конфиге — опровергается открытием файла, и дешёвая модель
ошибается здесь проверяемо; находка-суждение опровергается рассуждением, а
рассуждение стоит триажа или человека. Второй род ошибки — **пропуск**: он не
стоит ничего сегодня и не виден вовсе, и проход, у которого дороже пропустить,
держится наверху, даже будучи applicative.
Модель задана во frontmatter каждого агента, менять её здесь не нужно.
| Модель | Цвет | Проходы | Почему |
|---|---|---|---|
| `sonnet` | green | gate, code, ops | вход структурный, критерий записан заранее |
| `opus` | yellow | specs, adversary, rubric, basics, architecture, triage | суждение без опоры на инструмент |
| `sonnet` | green | scope, gate, ops | вывод перечислим и сверяется механически |
| `opus` | yellow | specs, code, basics, adversary, rubric, architecture, triage | дорога ошибка — ложная либо пропущенная |
**Цвет charter'а кодирует модель, а не роль прохода.** Это единственное
назначение цвета: список агентов читается взглядом, и по нему сразу видно, чем
@@ -122,17 +159,31 @@ charter'а, а модель потом двигает калибровка, и
модели **дороже** `opus` не обнаружилось ни на одном проходе, а прогон на ней
стоил заметно дольше и дороже — значит платить за неё не за что.
Двое из шести держатся на `opus` по признаку, отдельному от суждения: **их ошибка
распространяется дальше собственной находки.** Понижать их до `sonnet` вместе с
остальными дешёвыми проходами нельзя.
Четверо держатся наверху не за суждение, а по отдельным причинам, и их стоит
знать поимённо:
- `triage` — через него проходит всё, что оркестратор реализует **молча**:
ложноположительная находка становится кодом, потерянный `critical` — дефектом.
Ошибка триажа дороже ошибки любого отдельного прохода.
- `specs` — по устройству applicative, но направление `code → spec` требует
заметить **отсутствие**: тихий фолбэк, самодеятельный дефолт, проглоченную
ошибку. Здесь дорог пропуск, а не ложная находка.
- `code` — единственный, кто читает код **как код**. Его пропуск это дефект в
проде, и он тоже не оставляет следа ни в отчёте, ни в границах покрытия. По той
же причине, что `specs`, и это дороже всего в конвейере: проход идёт на каждой
задаче.
- `architecture` — запускается только в верхней ступени, на 5–10% задач, потолок
в 3 находки делает его дешёвым по выходу, а находка на предложении стоит абзаца
против переписывания на готовом коде. Дёшево × высокое плечо.
**`scope` внизу, и это не противоречие, хотя его ошибка расходится дальше всех.**
Его работа распадается надвое: поиск документов и раздача тем **перечислимы**
план сверяется с `ls docs/` за секунду, пропущенный документ виден без
рассуждения; выбор ступени — суждение, но у него есть три независимых
корректора: отрицательный тест `quick`, правило «спорный случай вниз» и сигнал
`basics` о заниженной ступени. Дешёвая модель безопасна ровно потому, что её
вывод устроен как список, а не как мнение.
**Самая дешёвая модель не используется ни на одном проходе, и это не экономия
наоборот.** Дешёвая модель на опиниативном проходе даёт правдоподобные находки,
которые триаж обязан опровергать оракулом, — а это самая дорогая операция
@@ -140,49 +191,73 @@ charter'а, а модель потом двигает калибровка, и
покрытие диффа, карта проекта — это скрипты проекта, они стоят ноль токенов.
Дешёвому проходу просто не осталось работы.
Экономия достигается не понижением модели, а **непуском прохода**: `quick`
четыре прохода, `standard` — пять, `wide` — семь. Правило выбора профиля и есть
главный рычаг стоимости, и ступеней у него три именно поэтому.
Экономия достигается **не понижением модели, а глубиной и непуском**: `quick` и
`standard` закрывают все темы, но чтением и рассуждением, а `wide` добавляет
доказательство — запуск, замер, построенный путь. Именно доказательство и стоит
часов: машина, цепочка меряющих проходов, ожидание.
Второй рычаг, помимо непуска,**вход и потолок прохода**, и он же объясняет
`basics` на `opus`. «Проход среднего усилия» тут значит не дешёвую модель, а
узкий вход (дифф и его окрестности, без карты проекта) и жёсткий потолок находок.
Прогон он ускоряет тем, чего **не** делает: ничего не запускает, ничего не меряет,
машину не держит — а именно замеры и цепочка меряющих проходов и составляли те
самые долгие часы.
Второй рычаг — **вход и потолок прохода**. `basics` идёт на верхней модели, но с
узким входом (дифф и его окрестности, без карты проекта) и жёстким потолком: 2
находки на сверке, 4 на разборе. Дешевле он не от модели, а от того, чего **не**
делает.
## Профили
**Ступень не меняет список тем — она меняет их глубину.** Все темы закрыты во
всех профилях; разница в том, читают ли их, рассуждают над ними или доказывают
запуском.
| Тема | `quick` | `standard` | `wide` |
|---|---|---|---|
| `requirements` | `specs` | `specs` | `specs` |
| `autotests` | `gate` | `gate` | `gate` |
| `conventions` | `code` | `code` | `code` |
| `architecture` | `basics`, сверка | `basics`, разбор | `architecture`, доказательство |
| `security` | `basics`, сверка | `basics`, разбор | `adversary`, доказательство |
| `operations` | `basics`, сверка | `basics`, разбор | `ops`, доказательство |
| тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор |
Отсюда состав:
| Профиль | Когда | Стадии | Проходов | Доля задач |
|---|---|---|---|---|
| `quick` | мелкое: багфикс, мелкая фича, локальная правка, доки | 0, 1, 4 | 4 | много |
| `standard` | **рабочее умолчание**: всё, что не мелкое и не крупное | 0, 1, 2, 4 | 5 | большинство |
| `wide` | крупное или незнакомое: большой рефакторинг, функциональность, форму которой ещё предстоит нащупать | 0, 1, 3, 4 | 7 | **510%** |
| `quick` | мелкое: багфикс, мелкая фича, локальная правка, доки | 0, 1, 2, 3, 5 | 6 | много |
| `standard` | **рабочее умолчание**: всё, что не мелкое и не крупное | 0, 1, 2, 3, 5 | 6 | большинство |
| `wide` | крупное или незнакомое: большой рефакторинг, функциональность, форму которой ещё предстоит нащупать | 0, 1, 2, 4, 5 | 8 | **510%** |
| `design` | **до кода**, на предложении | specs, плюс rubric и architecture по условию `wide` | 13 | — |
**`quick` и `standard` совпадают составом и различаются глубиной** — это
единственное место конвейера, где профиль не выводится из одного лишь списка
проходов. Поэтому глубина объявляется в отчёте наравне с профилем, а план
разметчика называет её по каждой теме. Проверять надо два факта вместо одного, и
оба напечатаны.
**Три глубины, и они не про старательность, а про способ доказательства.**
**Сверка** — открыть дом темы, открыть дифф, сравнить. **Разбор** — построить
сценарий рассуждением, ничего не запуская. **Доказательство** — прогнать,
померить, построить путь. Только третья требует машины, и только она стоит часов.
**`wide` назван по тому, что он добавляет: вход шире диффа.** Он единственный, где
живут тяжёлые проходы — враждебный, эксплуатационный и архитектурный, — и
единственный, где что-то **запускается и меряется**. Отсюда и его доля: три прохода
на стадии 3, два из них держат машину и потому идут цепочкой, а не разом. Это и
есть те самые долгие часы, и платить их каждой задаче не за что.
живут тяжёлые проходы, и единственный, где что-то **запускается**. `basics` в нём
берёт только проектные темы; своих тем у проекта нет — он не запускается вовсе, и
план говорит об этом строкой.
**Доля 5–10% — не пожелание, а проверка правила.** Она не считается механически, но
читается по журналу: если `wide` уходит каждая третья задача, ступень выбирают по
ощущению важности, а не по факту изменения. Обратный перекос виден иначе — по
журналу проскочивших дефектов в `docs/review.md`: класс, который ловят только
меряющие проходы, начинает всплывать после мерджа.
**Доля 5–10% — не пожелание, а проверка правила.** Если `wide` уходит каждая
третья задача, ступень выбирают по ощущению важности. Обратный перекос виден по
журналу проскочивших дефектов: класс, который ловят только меряющие проходы,
начинает всплывать после мерджа.
**Состав сверяется по этой таблице до коммита.** Реестр из трёх-семи проходов
проверяется взглядом — и это единственная защита от промаха, который уже
случился: пропуск прохода **не отличим от прохода без находок** (гейт зелёный,
спеки сошлись, отчёт выглядит полным), а заметить его мог бы только триаж,
который сам заполняется тем, что ему подали. Отчёт обязан перечислять запущенные
проходы **поимённо и с исходом**; непущенный идёт строкой «не запускался» в
границы покрытия, а не отсутствует. Цена молчащего пропуска измерена: семь
**Состав сверяется до коммита — по плану разметчика, а не по этой таблице.** План
и есть реестр: тема, дом, глубина, кто закрывает. Это единственная защита от
промаха, который уже случился: пропуск **не отличим от прохода без находок** (гейт
зелёный, спеки сошлись, отчёт выглядит полным), а заметить его мог бы только
триаж, который сам заполняется тем, что ему подали. Непущенное идёт строкой «не
запускался» с причиной, а не отсутствует. Цена молчащего пропуска измерена: семь
находок и отдельная задача на их дозакрытие.
Правило выбора — **два вопроса по факту изменения, не по ощущению важности**.
Отвечать по порядку, первый подошедший ответ и есть профиль:
Дом правила здесь, а применяет его `review-scope` на стадии 0 — не автор
изменения. Отвечать по порядку, первый подошедший ответ и есть профиль:
1. **Изменение крупное или незнакомое?**`wide`. Крупное — трогает несколько
узлов или слоёв разом, переносит ответственность между ними, перекладывает
@@ -221,17 +296,17 @@ charter'а, а модель потом двигает калибровка, и
стоит находки, которая всплывёт на следующей задаче или в журнале дефектов.
Ошибка в обратную стоит трёх тяжёлых проходов, двое из которых держат машину и
идут цепочкой, — и платится она **на каждой** задаче, выбранной неверно.
- **Спорно между `quick` и `standard` → бери `standard`.** Здесь разница в один
дешёвый проход, зато он единственный, кто на этих ступенях вообще смотрит на
отказы и на эксплуатацию.
- **Спорно между `quick` и `standard` → бери `standard`.** Здесь состав тот же, и
разница только в глубине трёх тем: сверка против разбора. Дёшево, и потому
сомнение решается в пользу разбора.
**Выбор сделан в пользу пропускной способности, и это записано, а не подразумевается.**
Конвейер настроен на поток задач, а не на максимум находок с каждой: поправить в
следующей задаче дешевле, чем держать одну два часа. Отсюда три обязанности,
без которых сделка превращается в незаметную потерю качества:
- **границы покрытия называют непущенные проходы поимённо** — иначе `quick`
выглядит так же, как `wide` без находок;
- **границы покрытия называют темы и их глубину**, а не только запущенные
проходы — иначе `quick` выглядит так же, как `wide` без находок;
- **журнал дефектов в `docs/review.md` перестаёт быть хорошей практикой и
становится единственной обратной связью**: проскочивший дефект — единственный
сигнал, что ступень выбрана слишком низко;
@@ -246,46 +321,50 @@ charter'а, а модель потом двигает калибровка, и
часть, которая сама по себе была бы `quick`.
Обратное тоже верно и тоже не бесплатно: у каждой задачи есть **несокращаемый
костяк из четырёх проходов** (гейт, спеки, код, триаж). Разрезать задачу, обе
половины которой остаются в одном профиле, — значит заплатить костяк дважды за ту
же проверку. Резать стоит там, где разрез **снимает дорогой проход с большей
части диффа**. Шов и правило нарезки живут у того, кто ведёт задачи, — скилл
`av-dev-pm:tasks`, его `references/split.md`. Пути туда конвейер не выносит: за
пределы своего плагина он ходит вызовом скилла, а не файлом.
костяк из шести проходов** (разметка, гейт, спеки, код, темы, триаж). Разрезать
задачу, обе половины которой остаются в одном профиле, — значит заплатить костяк
дважды за ту же проверку. Резать стоит там, где разрез **снимает доказательство с
большей части диффа**. Шов и правило нарезки живут у того, кто ведёт задачи, —
скилл `av-dev-pm:tasks`, его `references/split.md`. Пути туда конвейер не
выносит: за пределы своего плагина он ходит вызовом скилла, а не файлом.
Профиль объявляется в отчёте. Понижение профиля — решение оркестратора, и оно
попадает в границы покрытия строкой «профиль понижен до X, потому что …».
**Профиль и глубина объявляются в отчёте, и оба с обоснованием.** Ступень
выбирает `review-scope`; он вправе и поднять, и понизить её — но не молча: строка
«ступень X, потому что …» обязательна на каждом прогоне, а не только когда
ступень отличается от ожидаемой.
## Порядок прогона — граф, а не очередь
Профиль отвечает «какие проходы», порядок — «что кого ждёт». Стадии остаются
единицей **состава** (профиль набирается стадиями, см. таблицу выше), но порядок
задают **не их номера**: между стадиями 13 настоящих зависимостей нет — ни один
проход не читает вывод другого, — и очередь между ними была бы платой ни за что.
Профиль отвечает «какие темы и на какой глубине», порядок — «что кого ждёт».
Стадии остаются единицей **состава**, но порядок задают **не их номера**: между
стадиями 24 настоящих зависимостей нет — ни один проход не читает вывод другого,
— и очередь между ними была бы платой ни за что.
Рёбер два вида, и они разной природы. Путать их нельзя: первое про
**осмысленность** (на красном гейте опиниативный проход не о чем), второе про
**железо**.
**осмысленность** (без плана задание не определено, на красном гейте опиниативный
проход не о чем), второе про **железо**.
| Ребро | Смысл | Между кем |
|---|---|---|
| **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | гейт → все опиниативные; все проходы → триаж |
| **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | разметка → все; гейт → все опиниативные; все проходы → триаж |
| **конфликт за ресурс** | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» |
```mermaid
flowchart TD
gate["gate<br/>(стадия 0, держит машину)"]
scope["scope — разметка<br/>(стадия 0, темы и ступень)"]
gate["gate<br/>(стадия 1, держит машину)"]
specs["specs"]
code["code"]
basics["basics<br/>(standard)"]
basics["basics<br/>(quick, standard: темы;<br/>wide: только свои темы проекта)"]
adversary["adversary<br/>(wide, держит машину)"]
ops["ops<br/>(wide, держит машину)"]
architecture["architecture<br/>(wide)"]
triage["triage — единственный сток"]
scope -->|план| gate
gate -->|зелёный| specs
gate -->|зелёный| code
gate -->|"зелёный, standard"| basics
gate -->|"зелёный, темы по плану"| basics
gate -->|"зелёный, wide"| adversary
gate -->|"зелёный, wide"| ops
gate -->|"зелёный, wide"| architecture
@@ -299,11 +378,11 @@ flowchart TD
```
Читается граф так: **всё, у чего входящие рёбра закрыты, уходит одним
сообщением**. В `quick` после зелёного гейта это `specs` и `code` разом, и сразу
триаж. В `standard` к ним третьим добавляется `basics` — все трое уходят одним
сообщением, ждать друг друга им нечего. В `wide` вместо `basics` идут три тяжёлых:
`architecture` и первый из меряющей пары — сразу, второй меряющий — следом за
первым, и он же определяет, когда стартует триаж.
сообщением**. Разметка идёт первой и одна — до неё неизвестно ни что проверять,
ни на какой ступени. В `quick` и `standard` после зелёного гейта уходят разом
`specs`, `code` и `basics`, и сразу триаж. В `wide` вместо тем `basics` идут три
тяжёлых: `architecture` и первый из меряющей пары — сразу, второй меряющий —
следом за первым, и он же определяет, когда стартует триаж.
**Схема здесь старше прозы.** Она не иллюстрация к тексту, а сам алгоритм
планировщика; проза ниже объясняет рёбра и называет их цену. Разошлись — прав
@@ -330,7 +409,7 @@ flowchart TD
| `adversary` | да | находка есть **построенный путь**: он пишет падающий тест и гоняет его |
| `ops` | да | доказывает числами: время удержания блокировки, пик кучи, темп роста журнала |
| `triage` | да | проверяет оракул `critical`/`major` запуском — но он сток и тоже один |
| `specs`, `code`, `basics`, `architecture`, `rubric` | нет | читают и рассуждают, ничего не исполняют |
| `scope`, `specs`, `code`, `basics`, `architecture`, `rubric` | нет | читают и рассуждают, ничего не исполняют |
**Правило про ресурс, а не про имена.** Раньше здесь стояло именованное
исключение «`adversary` и `ops`»; оно рассыпается, как только проход начнёт
@@ -387,10 +466,33 @@ flowchart TD
Режим объявляется в отчёте наравне с профилем: **`по графу`** — одним словом,
**`линейно`** — с причиной (какой именно из трёх).
## Стадия 0 — Gate (обязательна во всех профилях)
## Стадия 0 — Разметка (обязательна во всех профилях)
Агент `review-gate`. Запускает команду гейта из семантики гейта в `CLAUDE.md` и
интерпретирует вывод.
Агент `review-scope`. Идёт **первым, до гейта**, и один: до его плана неизвестно
ни что проверять, ни на какой ступени.
Возвращает **план прогона**: список тем с домами и глубинами, ступень с
обоснованием, перечень документов, не ставших темами, и строку про найденные
директивы (`CLAUDE.md`, `AGENTS.md`). План уезжает в отчёт целиком и служит
границами покрытия.
**Он не судит по существу** — ни одной находки об изменении. Его ошибка это
пропущенная тема или не та ступень, и обе видны: план сверяется с `ls docs/` за
секунду, а заниженную ступень ловит `basics` своим сигналом.
**Ступень выбирает он, а не автор изменения.** Раньше профиль называл тот же
оркестратор, который писал код: он же решал, насколько глубоко его проверять, — и
разведённости с автором в этой точке не было вовсе. Вызывающий пайплайн профиль
больше не передаёт; он передаёт change, базу диффа и режим.
Право у разметчика симметричное: **поднять и понизить ступень он может
одинаково**, но обоснование обязательно в обоих случаях и всегда — строкой, какой
признак сработал и по какому факту.
## Стадия 1 — Gate (обязательна во всех профилях)
Агент `review-gate`. Закрывает тему `autotests`. Запускает команду гейта из
семантики гейта в `CLAUDE.md` и интерпретирует вывод.
**Пока гейт красный — опиниативные проходы не запускаются.** Оркестратор чинит и
перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки
@@ -408,69 +510,81 @@ flowchart TD
Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу
запрещено списывать такой отказ в мелочь.
## Стадия 1Conformance (обязательна во всех профилях)
## Стадия 2Сверка (обязательна во всех профилях)
Два applicative-прохода: оба применяют **записанный** критерий, оба дешёвые.
Машину не держат ни один, ребра между ними нет — уходят одним сообщением сразу
после зелёного гейта, вместе со стадией 2 или 3 — той, что в профиле.
Два прохода, оба против **записанного** критерия. Машину не держат ни один, ребра
между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со
стадией 3 или 4 — той, что в профиле.
- `review-specs` — критерий взят из **дельта-спек предлагаемого изменения**, а не
из proposal, сообщения коммита или описания задачи. Сверка двунаправленная;
направление `code → spec` важнее.
- `review-code` — критерий взят из конвенций проекта, каталог
`docs/conventions/`. Берётся только та их часть, которая **не выражается
правилом**: механизируемое уже проверила стадия 0. Что именно механизировано,
перечисляет `conventions/README.md` — повторять это проходом вредно.
- `review-specs` закрывает тему `requirements`. Критерий взят из **дельта-спек
предлагаемого изменения**, а не из proposal, сообщения коммита или описания
задачи. Сверка двунаправленная; направление `code → spec` важнее.
- `review-code` закрывает тему `conventions` **и делает технический разбор
кода** — это две его половины. Первая ищет дефект, который сработает сам, на
обычном входе: необработанная ветка отказа, пустое значение, граница диапазона,
перепутанный операнд, неосвобождённый ресурс, неверно применённый интерфейс
библиотеки. Вторая сверяет с конвенциями проекта, беря только ту их часть,
которая **не выражается правилом**: механизируемое уже проверила стадия 1.
Recall обоих равен длине их источника — это и есть предел applicative-проходов,
ради которого существуют стадии 2 и 3.
**Технический разбор — не тема, а обязанность прохода, и он единственный.**
Остальные читают код как материал для своей оптики: `specs` — против требований,
`basics` — против отказов окружения, `architecture` — против устройства. «Здесь
ошибка в логике» не говорит больше никто, и до недавнего времени не говорил
никто вовсе: `code` был проходом только по конвенциям, а дефект ловился разве что
случайно. Это была самая крупная дыра конвейера, и стоила она дороже любой
недосмотренной темы.
**`specs` дороже соседа по стадии, и это не недосмотр.** По устройству он тоже
применяет записанный критерий, и по таблице моделей ему полагался бы `sonnet`.
Держит его наверху **направление `code → spec`**: там надо заметить не нарушение
записанного, а **поведение, которого в спеке нет вовсе** — тихий фолбэк,
самодеятельный дефолт, проглоченную ошибку. Заметить отсутствие дороже, чем
сверить наличие, а ошибка здесь молчит: пропущенное поведение не оставляет следа
ни в отчёте, ни в границах покрытия. Прочие опиниативные проходы держат `opus`
из-за цены **ложных** находок; этот — из-за цены пропущенных.
Recall темы `conventions` равен длине конвенций проекта — это предел любой
сверки, и ровно ради него существуют стадии 3 и 4.
## Стадия 2 — Базовый проход (только `standard`)
**Оба прохода на верхней модели, и по одной причине — цене пропуска.** У `specs`
это направление `code → spec`: надо заметить **отсутствие** — тихий фолбэк,
самодеятельный дефолт, проглоченную ошибку. У `code` это пропущенный дефект,
который поедет в прод. Ни то ни другое не оставляет следа ни в отчёте, ни в
границах покрытия; прочие опиниативные проходы держат `opus` из-за цены **ложных**
находок, эти двое — из-за цены пропущенных.
## Стадия 3 — Темы (`quick`, `standard`; в `wide` — только свои темы проекта)
Агент `review-basics`. Один проход, машину не держит, ничего не запускает и не
меряет — уходит одним сообщением вместе со стадией 1, сразу после зелёного гейта.
меряет — уходит одним сообщением вместе со стадией 2, сразу после зелёного гейта.
**Он не самостоятельная оптика, а мелкая осадка двух тяжёлых проходов.** Берёт из
эксплуатационного — вопросы, на которые отвечают чтением, а не замером: есть ли
таймаут и отличит ли отправитель «медленно» от «упало»; идемпотентна ли повторная
операция и не теряют ли данные две одновременные; читает ли старый код новую схему
после частичного отката; что останется, если процесс остановят между шагами;
увидит ли человек, что поток оборвался ночью. Берёт из архитектурного — только то,
что видно рядом с диффом: не появился ли **второй способ** делать то, что уже
делается, мимо единой точки проекта, и **что опытный человек отсюда удалил бы**.
**Он не самостоятельная оптика, а держатель тем, у которых на этой ступени нет
своего проходчика.** На `quick` и `standard` это `security`, `operations` и
`architecture`: их именные проходы живут в `wide`, и без `basics` эти темы на
большинстве задач не смотрел бы никто. На любой ступени, включая `wide`, он же —
**приёмник проектных тем**: именных проходов конечное число, а тем столько,
сколько заведёт проект.
Чего он **не** берёт — и это записано в его уставе отдельным разделом: замеров,
эксперимента против драйвера и библиотеки, построенного пути атаки, карты проекта,
границы домена, направления зависимостей. Всё это стоит машины или входа шире
диффа, то есть ровно того, ради чего и существует `wide`.
Глубина приходит из плана: **сверка** (открыть дом темы, открыть дифф, сравнить;
потолок 2 находки) или **разбор** (построить сценарий рассуждением; потолок 4).
Чего он не делает ни на какой глубине — замеров, эксперимента против драйвера,
построенного пути, карты проекта, границы домена. Всё это стоит машины или входа
шире диффа, то есть ровно того, ради чего существует `wide`.
**Он покрывает миграцию и публичный контракт на `standard`.** Это не побочный
эффект, а условие, при котором миграция схемы вообще может не поднимать ступень:
её шаг гоняет `gate`, спеку сверяет `specs`, а вопросы «обратима ли», «что с
записями новой версии после отката» задаёт здесь `basics`. Уберёшь его — и
`standard` останется без единственного прохода, который смотрит на ось времени.
**Он покрывает миграцию и публичный контракт на нижних ступенях.** Это не
побочный эффект, а условие, при котором миграция схемы вообще может не поднимать
ступень: её шаг гоняет `gate`, спеку сверяет `specs`, а вопросы «обратима ли» и
«что с записями новой версии после отката» задаёт здесь `basics`, темой
`operations`. Уберёшь его — и нижние ступени останутся без единственного прохода,
который смотрит на ось времени.
Потолок — **4 находки** плюс короткая секция «дешевле переделать до мерджа».
Потолок и узкий вход и есть его «среднее усилие»: модель у него верхняя, потому
что дешёвая на опиниативном проходе платит триажем (см. «Модель по проходу»).
**В `wide` он запускается только при своих темах проекта.** Нет таких — план
говорит строкой «`basics` не запускается: все темы разобраны именными проходами».
Это единственное место, где состав не выводится из профиля, и потому оно
называется в плане явно.
## Стадия 3Тяжёлые проходы (только `wide`)
## Стадия 4Доказательство (только `wide`)
Три прохода, и все три уходят сразу после зелёного гейта, в одном ряду со
стадией 1:
стадией 2. Каждый берёт свою тему и доводит её до **доказательства**:
- `review-adversary` — находка есть **построенный путь**, а не свойство;
- `review-ops` — постмортем от симптома у владельца сервиса к строке кода;
- `review-architecture` — концептуальная целостность на входе шире диффа.
- `review-adversary`, тема `security` — находка есть **построенный путь**, а не
свойство: он пишет падающий тест и гоняет его;
- `review-ops`, тема `operations` — постмортем от симптома у владельца сервиса к
строке кода, с числами;
- `review-architecture`, тема `architecture` — концептуальная целостность на
входе шире диффа.
**Первые двое помечены «держит машину», поэтому между ними ребро конфликта: они
идут цепочкой, а не разом** (правило и его причина — в «Порядок прогона», раздел
@@ -496,11 +610,11 @@ Recall обоих равен длине их источника — это и е
ступенях, названо в «Честном пределе» и обязано идти строкой в границы покрытия
каждого прогона `quick` и `standard`.
Материал берётся из документов: `docs/security.md` — враждебному,
`docs/architecture.md`, `docs/research/` и `docs/database.md`
эксплуатационному, `docs/passport.md` и карта проекта — архитектурному. Что с чем
сшивать и почему — [project-facts.md](references/project-facts.md), раздел
«Сшивать обязаны проходы». Без этих документов стадия вырождается в общие места.
Дома тем приходят из плана разметчика: `security` — враждебному, `operations`
(эксплуатация, хранилище, числа) — эксплуатационному, `architecture` (устройство,
граница домена, решения) — архитектурному. Что с чем сшивать и почему —
[project-facts.md](references/project-facts.md), раздел «Сшивать обязаны
проходы». Без домов стадия вырождается в общие места.
**Условие стадии и есть условие ступени `wide`:** изменение крупное или
незнакомое. У архитектурного прохода работа появляется тогда, когда трогается
@@ -522,12 +636,19 @@ Recall обоих равен длине их источника — это и е
конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс
секция «дешевле переделать до мерджа».
## Стадия 4 — Triage (обязательна)
## Стадия 5 — Triage (обязательна)
Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.**
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
стартует. Получает сырые выводы всех проходов, `git diff`, профиль, режим и
**список запущенных проходов**; возвращает финальный отчёт.
стартует. Получает сырые выводы всех проходов, `git diff`, режим и **план
разметчика**; возвращает финальный отчёт.
**План на входе у триажа — не формальность, а сверка.** Он единственный, кто
видит и то, что размечено, и то, что пришло: «тем размечено шесть, отчёты
покрывают пять» — находка о самом прогоне, и заметить её больше некому. Раньше он
получал список запущенных проходов и потому мог сверить только состав; теперь
сверяет **темы**, а тема, оставшаяся без отчёта, — это то, чего список проходов
никогда не показывал.
Отсюда же правило, которое иначе выглядит придиркой: **триаж на неполном графе не
запускается**. Прогон, остановленный на полпути находкой «переделать форму», до
@@ -568,10 +689,11 @@ Recall обоих равен длине их источника — это и е
мелкой нарезке это самая большая статья конвейера. Рубрика же на узел знакомого
рода порождает свойства уже существующего рода — те, что и так записаны
конвенциями и спеками; а `architecture` на мелкой правке отвечает «нет» на свой
главный вопрос ещё до запуска (см. «Стадия 3»).
главный вопрос ещё до запуска (см. «Стадия 4»).
**Граф этого профиля свой, и он плоский.** Гейта нет — кода ещё нет, запускать
нечего; машину не держит ни один проход; сток — не триаж, а шаг 5 пайплайна
нечего; разметка сводится к одному вопросу (крупное или незнакомое), и его
задаёт вызывающий вместе с профилем; машину не держит ни один проход; сток — не триаж, а шаг 5 пайплайна
задачи, где замечания отрабатываются правкой спек. Триаж здесь не нужен: находок
единицы, и каждая либо правит спеку, либо становится развилкой.
@@ -654,8 +776,9 @@ flowchart TD
высказавшийся несколько раз. Совпадение повышает приоритет, но не `confidence`.
Что недоступно **этому** проекту принципиально — перечисляет «Недоступно
проверке» в `docs/review.md`, и оба его подраздела целиком уезжают в границы
покрытия.
проверке» в `docs/review.*`, по темам, и оба его подраздела целиком уезжают в
границы покрытия. **Тема, у которой нет дома, — тоже граница покрытия**, и она
объявляется планом на каждом прогоне, а не разово.
Независимо от проекта недоступно:
- поведение внешних систем в их будущих версиях;
@@ -682,6 +805,12 @@ flowchart TD
оракула не приносит и выше гипотезы находку не поднимает — кроме той, что
опирается на инвариант `CLAUDE.md`.
**Темы при этом закрыты все — разница в глубине, и её надо читать буквально.**
«Тема `security`, глубина сверка» не значит «безопасность проверена»: значит, что
дом темы открыли, дифф посмотрели и сравнили. Между сверкой и доказательством
лежит весь класс дефектов, который виден только построенным путём, — и он
проверяется на 5–10% задач.
Это сознательная сделка, а не пробел в устройстве: цена ступени `wide` платится на
каждой задаче, а окупается на немногих. Проверяется сделка не рассуждением, а
журналом дефектов: если класс, который ловят только меряющие проходы, начал
@@ -79,11 +79,14 @@ stateDiagram-v2
| Проход | Класс дефекта для инъекции | Заготовка пробы |
|---|---|---|
| `review-scope` | пропущенная тема | положить в `docs/` новый документ и проверить, попал ли он в план темой |
| `review-gate` | отсутствующая верификация | убрать тест на изменённую ветку, оставить код рабочим |
| `review-specs` | поведение вне спеки | добавить незаказанный фолбэк-дефолт на пустом входе |
| `review-code` | нарушение прозаической конвенции | увести штатный отказ мимо единой точки трансляции ошибки |
| `review-code` | технический дефект | не проверить возвращённую ошибку в ветке раннего возврата |
| `review-rubric` | нарушенное свойство узла | у клиента внешнего сервиса убрать таймаут и протяжку `context` |
| `review-basics` | отказ, видимый чтением | убрать обработку ошибки записи так, чтобы отказ считался успехом |
| `review-basics` | своя тема проекта | нарушить правило из документа, у которого нет именного прохода |
| `review-architecture` | второй способ | завести вторую точку генерации id мимо единой |
| `review-adversary` | построенный путь | принять внешний идентификатор без разбора до запроса в хранилище |
| `review-ops` | деградация окружения | убрать обработку недоступности внешней зависимости в фоновом цикле |
@@ -9,26 +9,42 @@
второй дом для тех же фактов разошёлся бы и выглядел актуальным.
Определение канона — в плагине `av-dev-pm`,
`skills/canon/references/canon.md`. Здесь только карта «что нужно проходу →
где это лежит».
`skills/canon/references/canon.md`. Здесь только карта «тема → её дом → что
оттуда берётся».
## Карта
## Карта тем
**Дом бывает файлом или каталогом**`docs/security.md` и `docs/security/`
называют одну и ту же тему. Форму дома называет план разметчика; проход её не
угадывает.
| Тема | Дом | Что оттуда берётся |
| --- | --- | --- |
| `requirements` | `openspec/specs/`, `openspec/changes/<id>/specs/` | нормативное поведение и дельты изменения |
| `autotests` | `CLAUDE.md`, семантика гейта | команда гейта, чем краснеет безусловно, чего в нём нет, кто гоняет дорогое |
| `conventions` | `docs/conventions.*` | конвенции прозой и **что уже механизировано** правилом |
| `architecture` | `docs/architecture.*` | компоненты и capability, единые точки проекта |
| | `docs/passport.*` | что система делает и **чего не делает**, граница домена |
| | `docs/adr/` | почему решено так, отвергнутые варианты |
| `security` | `docs/security.*` | периметр, недоверенный вход, из чего строятся пути и ключи, что вне модели |
| `operations` | `docs/architecture.*`, раздел эксплуатации | окружение, внешние зависимости поимённо, наблюдатель, характер потока |
| | `docs/database.*` | чем физически лежит запись, что при чтении и записи, настройки с числовым значением |
| | `docs/research/` | измеренные числа **с провенансом**, поведение внешних систем на самом деле |
| *тема проекта* | её документ в `docs/` | то, что проект счёл нужным записать |
Сквозное, не привязанное к теме:
| Что нужно проходу | Где лежит |
| --- | --- |
| что система делает и **чего не делает**, граница домена | `docs/passport.md` |
| инварианты **с severity рядом с формулировкой** | `CLAUDE.md`, раздел инвариантов |
| команда гейта, чем краснеет безусловно, чего в нём нет, кто гоняет дорогое | `CLAUDE.md`, семантика гейта |
| инварианты **с severity рядом с формулировкой** | `CLAUDE.md``AGENTS.md`, если он рядом), раздел инвариантов |
| что запускать запрещено, с путями; `testdata`; куда писать временное; имя основной ветки | `CLAUDE.md` |
| компоненты и capability, окружение, внешние зависимости поимённо, наблюдатель, характер потока, единые точки проекта | `docs/architecture.md` |
| чем физически лежит запись, что при чтении и записи, настройки с числовым значением | `docs/database.md` |
| периметр, недоверенный вход, из чего строятся пути и ключи, что вне модели | `docs/security.md` |
| измеренные числа **с провенансом**, поведение внешних систем на самом деле | `docs/research/` |
| конвенции прозой и **что уже механизировано** правилом | `docs/conventions/` |
| почему решено так, отвергнутые варианты | `docs/adr/` |
| типовые узлы, типовые ложноположительные, вопросы к проходам, **триггеры профиля**, недоступно проверке | `docs/review.md`, раздел настройки |
| прецеденты: воспроизведённые дефекты с оракулом | `docs/review.md`, журнал |
| нормативное поведение и дельты изменения | `openspec/specs/`, `openspec/changes/<id>/specs/` |
| типовые узлы, типовые ложноположительные, **вопросы по темам**, триггеры профиля, недоступно проверке | `docs/review.*`, раздел настройки |
| прецеденты: воспроизведённые дефекты с оракулом | `docs/review.*`, журнал |
**Вопросы проекта привязаны к теме, а не к имени прохода.** Раньше блок в
`docs/review.md` адресовался поимённо (`ops: <вопрос>`), и когда проход уехал в
верхнюю ступень, вопрос перестал задаваться молча. Тема переезд прохода
переживает.
## Сшивать обязаны проходы
@@ -50,9 +66,13 @@
**У `basics` стыков нет, и это не упущение.** Он не меряет, поэтому сшивать число
с настройкой ему нечего; единственное его основание для `critical` — инвариант из
`CLAUDE.md`, всё остальное он формулирует условиями и оставляет гипотезой. Его
вход намеренно узкий: единые точки и внешние зависимости из `docs/architecture.md`,
инварианты из `CLAUDE.md`, журнал из `docs/review.md`. Широкий вход — это профиль
`wide`, и там он есть у `architecture`.
вход намеренно узкий: дома тем из плана плюс инварианты и журнал. Широкий вход —
это профиль `wide`, и там он есть у `architecture`.
**У `scope` стыков нет по другой причине: он не читает содержимого.** Его дело —
найти дома и раздать темы, а не пересказать написанное. Пересказ сделал бы его
посредником между документом и проходом, а посредник расходится с источником и при
этом выглядит актуальным.
## Деградация — поразрядная
@@ -66,15 +86,17 @@
только **последствие** отсутствия, и оно называет самое дорогое, а не всех
пострадавших. Два списка читателей уже однажды разошлись; второго раза не надо.
| Нет документа | Что деградирует |
| Нет дома | Что деградирует |
| --- | --- |
| `CLAUDE.md` без инвариантов | `critical` по основанию «нарушен инвариант проекта» не присваивается никем |
| `docs/security.md` | `adversary` не знает периметра — формулирует условиями, `critical` не ставит |
| `docs/research/` | числа неизвестны `specs`, `ops`, `adversary` — формулируют условиями, а `specs` теряет проверку «требование против наблюдения» |
| `docs/database.md` | замер не с чем сравнить: находка не поднимается выше гипотезы |
| `docs/passport.md` | `architecture` теряет границу домена и вырождается в общее мнение |
| `docs/review.md` | `triage` отсеивает вслепую: типовых ложноположительных нет |
| `docs/architecture.md` | «не появился ли второй способ» не проверяется — единых точек не знает никто; `basics` теряет ещё и перечень внешних зависимостей |
| `docs/security.*` | тема `security` остаётся без дома: вопросы задаются по коду, `critical` не ставится, периметр неизвестен |
| `docs/research/` | числа неизвестны темам `operations` и `requirements` — формулируют условиями, а `specs` теряет проверку «требование против наблюдения» |
| `docs/database.*` | замер не с чем сравнить: находка темы `operations` не поднимается выше гипотезы |
| `docs/passport.*` | тема `architecture` теряет границу домена и вырождается в общее мнение |
| `docs/adr/` | «не отменяет ли изменение записанное решение» не спрашивает никто |
| `docs/review.*` | `triage` отсеивает вслепую: типовых ложноположительных нет; вопросы проекта по темам не задаются |
| `docs/conventions.*` | вторая половина `code` идёт вхолостую: записанных конвенций нет |
| `docs/architecture.*` | «не появился ли второй способ» не проверяется — единых точек не знает никто; тема `operations` теряет перечень внешних зависимостей |
Строка в границах покрытия обязана называть **причину**: «`docs/security.md` в
проекте нет» читается иначе, чем «есть, но периметр не назван». Без причины
@@ -80,9 +80,10 @@
факта, и карта их всех — [project-facts.md](project-facts.md): объём и
измеренное число → `docs/research/`; настройка хранилища → `docs/database.md`;
что необратимо и какой шаг гейта красит безусловно → `CLAUDE.md`; периметр и
недоверенный вход → `docs/security.md`. **Вопрос конкретному проходу**, если
промах лечится не фактом, а заданным вопросом, → раздел «Вопросы к проходам»
того же `docs/review.md`. Самый частый адрес и самый дешёвый. Прежде чем
недоверенный вход → `docs/security.*`. **Вопрос по теме**, если промах лечится
не фактом, а заданным вопросом, → раздел «Вопросы по темам» того же
`docs/review.*`; адресуй теме, а не имени прохода — проход уедет между
ступенями, тема останется. Самый частый адрес и самый дешёвый. Прежде чем
править charter, проверь, не хватит ли факта или вопроса: charter общий для
всех проектов, документ — про этот.
- **в конвенции или в правило линтера** — если свойство выражается
+27 -24
View File
@@ -104,7 +104,7 @@ description: Проводит несколько задач разом — пл
параллельном она гонится в волне **одна** (обоснование — ниже, в шаге 4).
Помечается здесь, на планировании, а не во время прогона, и **независимо от
режима**: состав волны определяется сейчас, режим может смениться просьбой уже
после плана, а профиль ревью сабагент выберет только внутри задачи — ключевать
после плана, а ступень ревью выберет разметчик уже внутри прогона — ключевать
волну на ещё не сделанный выбор нельзя. Триггеры — по фактам о задаче, каждый
сам по себе достаточен:
- трогает схему хранилища, миграцию, формат на диске или объём хранимого;
@@ -115,12 +115,12 @@ description: Проводит несколько задач разом — пл
где уже мерили или уже ломалось.
Ни один триггер не сработал — задача не замеряющая, даже если её ревью
окажется `wide`. Профиль про глубину проверки, замеряющая — про соревнование за
окажется `wide`. Ступень про глубину проверки, замеряющая — про соревнование за
железо; это разные вопросы, и совпадают они не всегда. Обратное тоже бывает и
тоже законно: помеченная задача, чьё ревью пошло профилем `quick` или
`standard`, машину не займёт вовсе — меряющие проходы живут только в `wide`.
Пометка от этого не снимается: она ставится **до** выбора профиля, и
перестраховка здесь стоит одной волны, а ошибка — испорченных чисел;
Пометка от этого не снимается: она ставится **до** разметки, и перестраховка
здесь стоит одной волны, а ошибка — испорченных чисел;
- **нумерованные артефакты — номера раздаёт оркестратор заранее.** Если проект
нумерует миграции (путь — `docs/.pm.json`, ключ `migrations`), посмотри последний
номер и **раздай номера всем задачам, которые, вероятно, их добавят**, до
@@ -238,9 +238,10 @@ flowchart TD
- прогони Skill **`av-dev-pipeline:task-pipeline`** ровно на этой задаче,
полный цикл SDD с обоими чекпоинтами ревью;
- если задаче назначен **номер артефакта** — используй строго его;
- **профиль ревью выбирается по факту изменения.** Батч не повод понижать
профиль: «нас много и мы спешим» — это ровно тот стимул, из-за которого
проходы пропускают;
- **ступень ревью выбирает разметчик конвейера, а не ты и не сабагент.**
Профиль в вызов не передаётся вовсе. Батч не повод её понижать: «нас много и
мы спешим» — ровно тот стимул, из-за которого проходы пропускают, и он снят
тем, что регулятор не в руках у автора;
- **режим прогона проходов ревью — от режима батча**, и его называет charter,
а не сабагент: батч идёт по одной задаче → режим умолчательный, **`по
графу`** (машина свободна); батч идёт волнами → **`линейно`**, твой worktree
@@ -248,17 +249,18 @@ flowchart TD
Внутренние рёбра графа — цепочку проходов, держащих машину — конвейер
соблюдает сам, в любом режиме;
- **если вложенные сабагенты недоступны** (движок не даёт запускать агентов из
агента) — не пропускай ревью и не понижай профиль: проведи его **инлайн** по
тем же charter'ам `av-dev-pipeline`, сохранив обязательное — гейт до
опиниативных проходов, состав по профилю, триаж последним. И **скажи в
отчёте прямым текстом, что ревью шло инлайн**: инлайновый проход видит
контекст автора и потому разведён с ним слабее — это меняет доверие к
результату, а не только способ запуска;
агента) — не пропускай ревью и не понижай ступень: проведи его **инлайн** по
тем же charter'ам `av-dev-pipeline`, сохранив обязательное — разметку первой
(план с темами и ступенью), гейт до опиниативных проходов, состав по плану,
триаж последним. И **скажи в отчёте прямым текстом, что ревью шло инлайн**:
инлайновый проход видит контекст автора и потому разведён с ним слабее — а
инлайновая разметка вдобавок означает, что ступень выбрал автор, и это
отдельная строка;
- **вернуть отчёт**, в котором обязательно: исход задачи одним из трёх слов;
**объявленный профиль ревью и режим прогона**; что сделано; какие вопросы
**план прогона: ступень с обоснованием, темы и их глубины**, и режим; что сделано; какие вопросы
записаны и куда; изменённые файлы; добавлялся ли нумерованный артефакт и с
каким номером; затронутые capability; состояние гейта; **перечень
запущенных проходов ревью поимённо с исходом каждого**; **путь к
тем с исходом по каждой**; **путь к
сохранённому отчёту триажа** (`openspec/changes/<id>/review/`, после
архивации — `openspec/changes/archive/<id>/review/`); шло ли ревью
инлайн; границы покрытия.
@@ -275,24 +277,25 @@ flowchart TD
### 5. Проверить полноту ревью — до интеграции
**Ветка, чей отчёт не называет профиль и проходы поимённо, не вливается.**
Пропуск прохода не отличим от прохода без находок, и на уровне батча это ещё
опаснее: отчётов много, каждый выглядит полным, а сверять их некому, кроме тебя.
**Ветка, чей отчёт не называет план прогона, не вливается.** Пропуск не отличим
от прохода без находок, и на уровне батча это ещё опаснее: отчётов много, каждый
выглядит полным, а сверять их некому, кроме тебя.
Сверка идёт в три шага, и порядок важен:
1. **Возьми объявленный профиль** из отчёта задачи — он затем и заказан в
обязательных полях шага 4. Профиля в отчёте нет — перечень проходов сверять
не с чем; это само по себе основание не вливать, пока сабагент не назовёт
профиль и не обоснует его по факту изменения.
1. **Возьми план прогона** из отчёта задачи — таблицу «тема → дом → глубина → кто
закрывает» со ступенью и обоснованием; он затем и заказан в обязательных полях
шага 4. Плана в отчёте нет — сверять не с чем; это само по себе основание не
вливать, пока сабагент не покажет план разметчика.
2. **Сверяй с независимым артефактом, а не с прозой отчёта.** Перечень проходов
бери из **сохранённого отчёта триажа** (`openspec/changes/<id>/review/` или
`openspec/changes/archive/<id>/review/` — задача доведена, change заархивирован) —
пайплайн обязан его туда положить. Проза сабагента написана тем же, кто мог
проход и пропустить: она подтверждает сама себя. Отчёта триажа на месте нет —
считай, что состав неизвестен, и дозапускай ревью целиком.
3. **Сверь состав** с таблицей профилей скилла
`av-dev-pipeline:review-pipeline` для объявленного профиля.
3. **Сверь план с исходом**: против каждой темы плана обязан стоять отчёт либо
названная причина его отсутствия. Раскладка «тема → кто закрывает на этой
ступени» — в скилле `av-dev-pipeline:review-pipeline`.
Расхождение — не повод отменять задачу: дозапусти недостающие проходы **на
ветке**, в её worktree, через `av-dev-pipeline:review-pipeline`, и только потом
+27 -21
View File
@@ -10,8 +10,8 @@ description: Автономно проводит одну задачу чере
Это тонкая обёртка над каноническими скиллами `opsx:explore` / `opsx:propose` /
`opsx:apply` / `opsx:archive` — вызывай их через Skill, не переизобретай их шаги.
Ревью — скилл `av-dev-pipeline:review-pipeline`, он же держит правило выбора
профиля.
Ревью — скилл `av-dev-pipeline:review-pipeline`; он же держит правило выбора
ступени и **сам её выбирает**: ты профиль не передаёшь.
## Предпосылки
@@ -76,8 +76,8 @@ description: Автономно проводит одну задачу чере
Задача сделана, когда верно всё:
1. гейт проекта зелёный;
2. ревью проведено **по профилю**, состав прогона сверен с таблицей профилей
поимённо, непущенные проходы названы в границах покрытия;
2. ревью проведено, **план прогона сверен с исходом по каждой теме**, темы без
отчёта и без дома названы в границах покрытия;
3. change заархивирован, дельты влиты в актуальные спеки;
4. коммит сделан в текущую ветку;
5. **критерии приёмки, если проект их дал, выписаны поимённо, и по каждому назван
@@ -160,7 +160,7 @@ flowchart TD
s4["4. ревью предложения, профиль design"]
s5["5. отработать замечания + validate --strict"]
s6["6. opsx:apply — код, гейт, поведенческая верификация"]
s7["7. ревью кода, профиль по факту изменения"]
s7["7. ревью кода, ступень выбирает разметчик"]
s8["8. opsx:archive"]
s9["9. синк документации — av-dev-pm:docs"]
s10["10. коммит работы — av-dev-git:commit"]
@@ -259,13 +259,18 @@ flowchart TD
### 7. Ревью кода — Skill `av-dev-pipeline:review-pipeline`
Второй чекпоинт. Вызови Skill **`av-dev-pipeline:review-pipeline`**, дав ссылку
на change `<id>`, базу диффа, профиль **и режим запуска**.
на change `<id>`, базу диффа **и режим запуска**.
**Правило выбора профиля живёт в скилле конвейера** (раздел «Профили»), проектные
триггеры — в `docs/review.md`, если записаны. Здесь оно не пересказывается: три
копии одного правила расходятся, и работать будет та, которую прочитали
последней. Помни ровно одно — **профиль выбирается по факту изменения, а не по
ощущению важности**, и посмотри таблицу перед вызовом.
**Профиль ты не передаёшь, и это правило, а не упрощение.** Ступень выбирает
разметчик конвейера (`review-scope`, стадия 0) — по объёму и незнакомости
изменения, с обоснованием строкой. Причина в разведённости: ты только что написал
этот код, и решать, насколько глубоко его проверять, тебе нельзя — под давлением
«я почти закончил» решение известно заранее. Правило выбора живёт в скилле
конвейера, проектные триггеры — в `docs/review.*`.
**Считаешь ступень заниженной — скажи это в докладе строкой, а не переспорь.**
Разметчик вправе и поднять, и понизить; твоё несогласие это факт для человека, а
не команда конвейеру.
**Режим по умолчанию — `по графу`, и обосновывать его не надо.** Конвейер сам
знает свои рёбра: гейт открывает опиниативные проходы, проходы с пометкой «держит
@@ -280,11 +285,12 @@ flowchart TD
потолком 7 пунктов, разметкой `Действие: инлайн | развилка` и секцией границ
покрытия.
**Сверь состав прогона с таблицей профилей в скилле, прежде чем коммитить.**
Отчёт обязан называть запущенные проходы **поимённо и с исходом**; непущенный
идёт строкой «не запускался» в границы покрытия. Реестр короткий (4–8 проходов) —
сверка стоит одного взгляда. Почему это правило существует, объясняет раздел
«Профили» скилла конвейера; здесь — само требование.
**Сверь план прогона с исходом, прежде чем коммитить.** Отчёт начинается планом
разметчика — таблицей «тема → дом → глубина → кто закрывает», — и против каждой
темы обязан стоять исход. Тема без отчёта и тема без дома — разные вещи, и обе
должны быть названы. Реестр короткий (шесть тем ядра плюс свои) — сверка стоит
одного взгляда. Почему это правило существует, объясняет раздел «Профили» скилла
конвейера; здесь — само требование.
Отработай так же, как шаг 5: помеченное `инлайн` чини сам и не логируй,
`развилка` — вопросом в запись (он уже сформулирован триажем, его остаётся
@@ -384,7 +390,7 @@ flowchart TD
это доклад приёмщику, а не отметка «принято»;
- **`Урожай`** — отложенные находки списком (формулировка, оракул, провенанс).
Задачи из него заводит тот, кто ведёт задачи проекта;
- **одна строка границ покрытия**: какой профиль и режим гонялись, какие проходы
- **одна строка границ покрытия**: какая ступень и режим гонялись, какие проходы
не запускались и что проверить было невозможно. Доклад без неё сообщает
«проверено», не сообщая, что именно.
@@ -402,7 +408,7 @@ flowchart TD
спрашивай, запиши вопросом и доведи остаток.
- Держи вызывающего в цикле короткими репликами на переходах фаз, но не проси
подтверждать механику.
- **Занизить профиль ревью или пропустить проход — самый дешёвый способ
«ускориться», и он же самый дорогой по последствиям.** Защита одна: профиль
выбирается по факту изменения, состав сверяется поимённо, а непущенное
называется в отчёте строкой.
- **Занизить ступень ревью или пропустить тему — самый дешёвый способ
«ускориться», и он же самый дорогой по последствиям.** Защита устроена так,
что регулятора у тебя нет: ступень выбирает разметчик, план сверяется по темам,
непокрытое называется в отчёте строкой.