Files
dev-skills/av-dev/skills/code-review/SKILL.md
T
av a4bc9191e7 хвост задачи: отражение молча, новое — по слову человека
Синк документации делил правки по документам, а делить их надо по роду.
Отражение сделанного (вливание дельт, миграция, компонент в обзоре) пишется
молча: без правки документ станет ложным. Новая запись и новая норма — ADR,
конвенция, записка разведки, инвариант, периметр, дефект в журнале — только
предлагаются, а пишет их третий такт шага 6 после слова человека.

Реплика при этом одна на весь хвост: вопрос про урожай ревью переехал с шага 5
на шаг 6 и слился с предложениями синка — решение одно, «что из найденного
переживёт задачу». Плановых стопов в сценарии решения стало ровно два, и оба
про решения человека.

Сверка документов получила счётчик: doc-healthcheck оставляет след ключом
[healthcheck] last в .av-dev.toml, синк считает по нему задачи с прошлого
прогона и говорит строкой. Прежде признак «десяток задач» держался в памяти,
то есть не срабатывал.

Журнал — тема 78.
2026-08-23 19:06:06 +03:00

981 lines
90 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: code-review
description: "Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Состав прогона постоянный, метки у него нет: гейт (autotests), сверка со спекой (specs), разбор кода и конвенций (code), триаж; приёмник тем (basics) идёт, когда у проекта есть свои темы. Цикл задачи проверяет корректность и механику против записанного критерия — дельта-спеки, конвенции, инварианты CLAUDE.md, вывод инструментов. Темы риска и устройства — security, operations, architecture — закрыты в цикле только сверкой с записанными инвариантами: их разбор, доказательство запуском и суждение о форме решения живут в скилле av-dev:code-deep-review, который идёт по области кода и время от времени. Порядок прогона — граф зависимостей: гейт открывает проходы с мнением, триаж — единственный сток. Находки по умолчанию чинятся инлайн и молча; человеку уходит только необратимое и то, что меняет дельта-спеки, а задачи из урожая заводятся по его слову. Проектная специфика приходит из документов канона проекта. Вызывается из скилла av-dev:code-resolve после apply. Второй вызов идёт от сценария обслуживания: без change, фиксированным планом (autotests, operations, плюс conventions, если тронут код)."
---
# Конвейер ревью
Готовит ревью — **не заменяет его**. Потребитель отчёта — оркестратор, который
чинит код; человек читает только сводку, развилки и границы покрытия.
## Четыре правила, из которых всё следует
Если ситуация не покрыта инструкцией — решай по ним.
0. **Тема первична, проход вторичен.** Ревью проверяет **темы** — набор
направлений, который проект объявляет своими документами. Проход это только
способ закрыть тему на заданной глубине, и проходы меняются: переезжают в
другой скилл, сливаются, упраздняются. Если состав прогона считать списком
проходов, то уехавший проход уносит тему с собой **беззвучно** — отчёт честно
скажет «`ops` не запускался» и не скажет «эксплуатацию не смотрел никто», а
нужно второе. Проверено на живом переезде: `ops` и `adversary` ушли в
`av-dev:code-deep-review`, а темы `security` и `operations` остались в
конвейере — узко, сверкой с инвариантами внутри `code`, и это записано
строкой. Поэтому прогон описывается таблицей «тема → кто закрывает → против
чего», и таблица эта есть в каждом отчёте.
1. **Recall чек-листа равен длине чек-листа.** Проход, устроенный как «проверь
пункты 1..N», найдёт ровно перечисленное. Всё неявное — идиомы, форма
решения, «так не делают» — неперечислимо по определению: перечислимое уже
стало бы конвенцией. Отсюда деление проходов на **applicative** (применяют
заданный критерий) и **generative** (сперва порождают критерий или
альтернативу, потом сравнивают). Расширять чек-листы бесполезно; неявный слой
достают только generative-проходы.
2. **Ценность верификатора = наличие внешнего оракула × разведённость с
автором**, а не число ролей. Под всеми ролями одна модель с одними
априорными, вход у всех общий: седьмая роль почти не добавляет recall, но
линейно удорожает триаж. Иерархия надёжности: детерминированный инструмент >
агент, который его **запускает** и интерпретирует вывод > агент с чистым
мнением. Максимум работы переносим вниз.
3. **Отчёт без границ покрытия хуже отсутствия отчёта.** «Критичных проблем не
обнаружено» потребляет ощущение проверенности, ничего не гарантируя. Секция
границ покрытия обязательна и не сокращается — в том числе в докладе человеку.
## Предпосылки
Конвейер опирается на внешнюю обвязку и без неё работает не целиком. Проверь это
один раз, при установке плагина в проект:
- **OpenSpec — жёсткая предпосылка, а не опция.** Проход `review-specs` и
вызывающий скилл `av-dev:code-resolve` завязаны на дельта-спеки
(`openspec/changes/<id>/specs/*/spec.md`), на актуальные спеки
(`openspec/specs/`) и на `openspec validate --strict`. В проекте без OpenSpec
шаги, зовущие `opsx:explore` / `opsx:propose` / `opsx:apply` / `opsx:archive`,
упадут на «нет такого скилла», а `review-specs` останется без источника
требований. **Проект без OpenSpec этим конвейером не проверяется** — подключай
OpenSpec, а не понижай прогон: ветка деградации здесь не пишется, потому что
непроверенная ветка деградации хуже честного отказа. Заводить руками не надо:
этим владеет скилл `av-dev:code-openspec` — он заводит каталог и заменяет
пример в `config.yaml` настройкой. Его же зовут `av-dev:doc-init` на новом
проекте и `av-dev:canon` в режиме `adopt` — на переводимом.
**Предпосылка эта — про изменение поведения, а не про всякий прогон:**
сценарий обслуживания зовёт конвейер без change и без дельта-спек, и ни один
проход его плана на них не завязан. См. «Прогон без change».
- **Документы канона** — см. следующий раздел.
- **Проектные копии этих скиллов и агентов удаляются при установке.**
<!-- копия: проектные-копии из README.md -->
Голые имена в `.claude/skills/`: `resolve`, `review`, а у проектов прошлого
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`. С префиксом
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`. Агенты:
`.claude/agents/<проект>-review-*.md`.
Две копии одного скилла расходятся, и побеждает та, что **короче названа**:
короткое имя разрешится в устаревшую проектную копию молча и без признаков
подмены.
<!-- /копия: проектные-копии -->
### Чего может не быть
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
Правится дом, а не этот файл.
<!-- копия: отсутствие из av-dev/shared/absence.md -->
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
живут порознь; каждая узнаётся своим следом:
| Чего нет | Как видно | Чего теперь не делает никто |
| --- | --- | --- |
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
**Свой скилл зовётся полным именем**`av-dev:canon`, `av-dev:task-track`,
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
поведении.
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
сам.
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
сделанного. Выдумывать обходной путь нельзя тоже.
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
<!-- /копия: отсутствие -->
Своих скиллов это касается ровно так же: `av-dev:code-review`,
`av-dev:code-resolve`, `av-dev:code-openspec` — подменяется короткое имя,
а не чужое.
## Темы, источники и процессные документы
Раньше здесь стояло плоское правило «каждый документ проекта — тема ревью». Оно
верно ровно наполовину, и потому вредно целиком: паспорт и схему хранилища
ревью читает, но темами они не являются, а журнал решений и журнал наблюдений
ревью изменения не нужны вовсе. Прогон, применявший правило буквально, обязан был
либо завести фантомные темы и продублировать ими работу настоящих, либо потерять
документ молча.
**Разрез один и проверяемый — тот же, что в каноне: можно ли по документу
сказать «в этом изменении сделано не так»?**
| Категория | Что конвейер с ней делает | Кто в ней |
|---|---|---|
| **тема** | заводит направление проверки и требует исполнителя | `conventions.*`, `security.*`, `architecture.*`, свои документы проекта |
| **источник темы** | читает как материал чужой темы, своей не порождает | `passport.*`, `database.*`, `CLAUDE.md`/`AGENTS.md`, `openspec/specs/` |
| **процессный документ** | не судит по нему изменение | `tasks/`, `docs/review.*`, `docs/adr.*`, `docs/research.*`, `.av-dev.toml` |
**Одна процессная запись всё же читается — `docs/review.*`.** В ней лежит
настройка самого конвейера: вопросы по темам, журнал дефектов, типовые узлы,
типовые ложноположительные. Проход, читающий её, читает **свою обвязку**, а не
критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не
открывает никто.
Дом канона этой раскладки — скилл `av-dev:canon`, раздел «Три категории
документов». Конвейер её **читатель**: категории и имена тем он берёт
оттуда и своих не заводит.
Отсюда то, ради чего правило и заведено: **`docs/` перестаёт быть просто
документацией и становится конфигурацией конвейера**. Проект настраивает ревью
тем, что пишет о себе, а не отдельным файлом настроек, который разошёлся бы с
документами. **Открыта при этом только категория `тема`** — две другие закрыты
и перечислены поимённо, поэтому документ, которого нет в раскладке канона,
однозначно своя тема проекта, а не «что-то непонятное».
Ядро — шесть тем, они есть у любого проекта, приведённого к канону. Форма дома
значения не имеет: `docs/security.md` и `docs/security/` — одна тема `security`.
| Тема | Дом | Вопрос темы |
|---|---|---|
| `requirements` | `openspec/specs/`, дельты change | делает ли код заказанное, и только его |
| `autotests` | `CLAUDE.md`: семантика гейта, команды | проверено ли машиной и хватает ли проверок |
| `conventions` | `docs/conventions.*` | написано ли так, как здесь пишут |
| `architecture` | `docs/architecture.*` + источник `passport.*` | цело ли устройство: понятия и границы |
| `security` | `docs/security.*` | что сделает недоверенный вход |
| `operations` | `docs/architecture.*`, раздел эксплуатации + источник `database.*` | что будет через неделю на проде |
**Три темы ядра дома в `docs/` не имеют, и это не пробел.** `requirements` живёт
в `openspec/`, `autotests` — в `CLAUDE.md`, `operations` — разделом внутри
`architecture.*`. Имя темы поэтому не выводится из имени файла, и обратно тоже:
`docs/passport.md` не заводит темы `passport`.
**`adr/` и `research/` прогон больше не открывает.** Раньше архитектурный проход
читал решения, а эксплуатационный и `specs` — числа. Цена решения записана в
каноне и повторяется здесь, потому что платит её конвейер: **расхождение
изменения с записанным решением прогоном не ловится**, это работа сверки
документации — скилл `av-dev:doc-healthcheck`.
Строка об этом обязательна в границах покрытия каждого прогона.
**Своя тема проекта бывает двух происхождений, и обе законны:** документ, который
проект положил в `docs/` и которого нет в раскладке канона, — и **тема, названная
директивой** `CLAUDE.md`/`AGENTS.md`, у которой документа нет вовсе. У второй дом
— сама директива; в остальном она ничем не отличается, и в раздаче идёт туда же.
Различать их приходится потому, что условие запуска приёмника тем звучит «есть ли
свои темы проекта», и тема без файла в `docs/` иначе не попала бы под это условие
никогда.
**Проектная тема закрывается `basics`**, и только она. Именных проходов конечное
число, а тем — сколько заведёт проект; приёмник обязателен, иначе открытость
списка была бы обещанием без механизма. Темы **ядра** он не держит вовсе:
`requirements` закрывает `specs`, `conventions` и технику — `code`, а риск и
устройство — тот же `code` сверкой с инвариантами. Отсюда правило состава:
**`basics` запускается тогда и только тогда, когда ему есть что принимать** — см.
«Состав прогона».
**Тема без дома — законное состояние и отдельная строка.** «Тема `operations`
заявлена, `docs/database.md` нет» читается иначе, чем «не смотрели». Деградация
поразрядная: нет дома — падает глубина этой темы, и только её.
Что именно проход читает по каждой теме — [references/project-facts.md](references/project-facts.md).
Отдельного файла-брифа при этом нет: пути известны, посредник не нужен, а второй
дом для тех же фактов разошёлся бы и выглядел актуальным.
**Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой
и предложи скилл `av-dev:canon`: одна операция на проект против деградации на
каждой задаче. Прогон при этом не останавливается.
## Что получает каждый проход
Задание собирается **по таблице тем** и состоит из шести вещей:
- **его темы** — какие темы он закрывает, у каждой **дом** (путь и раздел) и
**глубина**. Дом передаётся адресом, а не пересказом: проход, получивший
проинтерпретированный периметр, не заметит, что интерпретация неверна;
- **вопросы по его темам** из `docs/review.*`, если они там есть, — **дословно**.
Вопрос привязан к теме, а не к имени прохода, и потому переживает переезд
прохода между скиллами;
- **контракт находок** — путь к
[references/finding-contract.md](references/finding-contract.md) (в
установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/`);
- **изменение** — идентификатор change и путь к его дельта-спекам;
- **база диффа**;
- **его глубина и режим** прогона — чтобы проход знал, что писать в границы
покрытия.
**Ступень 1 получает сверх этого исход гейта, прогнанного до ревью** — сводку,
путь к логам шагов и отпечаток дерева, — если вызывающий скилл его дал. Зачем и
что происходит при расхождении — «Ступень 1 — Автотесты».
Чего проход **не** получает ни в каком режиме — выводов других проходов. См.
«Порядок прогона».
## Модель по проходу
Модель выбирается **по цене ошибки прохода, а не по его роду**. Признак рабочий и
проверяемый: находка со ссылкой на записанный источник — строку спеки, цель в
манифесте, значение в конфиге — опровергается открытием файла, и дешёвая модель
ошибается здесь проверяемо; находка-суждение опровергается рассуждением, а
рассуждение стоит триажа или человека. Второй род ошибки — **пропуск**: он не
стоит ничего сегодня и не виден вовсе, и проход, у которого дороже пропустить,
держится наверху, даже будучи applicative.
Модель задана во frontmatter каждого агента, менять её здесь не нужно.
| Модель | Цвет | Проходы | Почему |
|---|---|---|---|
| `sonnet` | green | autotests, ops | вывод перечислим и сверяется механически |
| `opus` | yellow | specs, code, basics, triage, rubric, adversary, architecture | дорога ошибка — ложная либо пропущенная |
**В таблице стоят и проходы, которых в цикле нет.** `adversary`, `ops` и
`architecture` работают в скилле `av-dev:code-deep-review`, `rubric` зовут прямо
руками; раскладка «модель — цвет» общая для всех уставов плагина и проверяется
механически, поэтому дом у неё один, а не по скиллу.
**Цвет charter'а кодирует модель, а не роль прохода.** Это единственное
назначение цвета: список агентов читается взглядом, и по нему сразу видно, чем
платит прогон. Роль прохода из имени и так понятна, а цвет, розданный по ролям,
не отвечает ни на один вопрос, который задают во время прогона. Раскладка живёт
здесь и **проверяется механически** — цвет ставится один раз при заведении
charter'а, а модель потом двигает калибровка, и разъезжаются они молча.
**Моделей две, и верхняя из них — `opus`; выше неё конвейер не платит.** Замер:
на первом же прогоне самые ценные находки дали `opus`-проходы — сверка спек дала
13 находок с оракулами, а проход про идиоматичность (впоследствии упразднённый) —
три эксперимента против драйвера БД с воспроизведёнными числами. Разницы в пользу
модели **дороже** `opus` не обнаружилось ни на одном проходе, а прогон на ней
стоил заметно дольше и дороже — значит платить за неё не за что.
Четверо держатся наверху не за суждение, а по отдельным причинам, и их стоит
знать поимённо:
- `triage` — через него проходит всё, что оркестратор реализует **молча**:
ложноположительная находка становится кодом, потерянный `critical` — дефектом.
Ошибка триажа дороже ошибки любого отдельного прохода.
- `specs` — по устройству applicative, но направление `code → spec` требует
заметить **отсутствие**: тихий фолбэк, самодеятельный дефолт, проглоченную
ошибку. Здесь дорог пропуск, а не ложная находка.
- `code` — единственный, кто читает код **как код**, и с уходом тяжёлых проходов
он же единственный, кто смотрит на риск и устройство. Его пропуск это дефект в
проде, и он не оставляет следа ни в отчёте, ни в границах покрытия. По той же
причине, что `specs`, и это дороже всего в конвейере: проход идёт на каждой
задаче.
- `basics` — держит темы, которые проект завёл сам, то есть ровно те, о которых
плагин ничего не знает. Ошибиться на чужой теме дешёвой моделью проще всего:
критерий приходит текстом документа, а не перечнем.
**Самая дешёвая модель не используется ни на одном проходе, и это не экономия
наоборот.** Дешёвая модель на проходе с мнением даёт правдоподобные находки,
которые триаж обязан опровергать оракулом, — а это самая дорогая операция
конвейера. Механизируемая же работа здесь вынесена **ниже** модели: гейт,
покрытие диффа, карта проекта — это скрипты проекта, они стоят ноль токенов.
Дешёвому проходу просто не осталось работы.
Экономия достигается **не понижением модели, а тремя другими рычагами**, и все
три применяются к каждому проходу с мнением, а не к одному избранному.
1. **Непуск.** Тяжёлые проходы в цикле не запускаются вовсе — они живут в
`av-dev:code-deep-review`; приёмник тем не идёт, когда своих тем у проекта
нет. Что при этом перестаёт проверяться, названо поимённо и идёт в границы
покрытия.
2. **Вход.** `basics` идёт на верхней модели, но с узким входом: дифф и его
окрестности, без карты проекта. Карта проекта и вход шире диффа не даются в
цикле никому — это цена глубокого прогона, а не задачи.
3. **Потолок.** Он есть у каждого прохода с мнением и напечатан: `basics` — 4
находки; `code` — 4 конвенционных и 1 на все три темы риска и устройства
разом, у технической половины потолка нет; `specs` — потолка нет; триаж — 7 в
основном списке. Двум половинам его не ставят намеренно: пропуск дефекта и
пропуск расхождения со спекой стоят дороже длинного списка.
Все три раньше зависели от метки и потому на каждой задаче считались заново.
Теперь они постоянные, и проход знает свой потолок до того, как получил задание.
Проход без потолка выдаёт столько находок, сколько нашёл поверхностей, — а это
ровно тот механизм, из-за которого был снят проход независимой реализации:
**счёт определялся объёмом вывода**. Потолок ставится не ради краткости отчёта, а
против этого.
**Потолок обязан быть объявлен, когда он сработал.** Проход, срезавший находки
до потолка, говорит об этом строкой в своих границах покрытия: сколько осталось
за срезом и какого рода. Молчащий срез неотличим от «больше не нашлось» — это тот
же класс молчащего пропуска, что и непущенный проход.
## Состав прогона — постоянный
**Ступени нумерованы и наружу не выходят.** Прогон ревью один, и зовёт его
`av-dev:code-resolve` после того, как код написан; членение внутри прогона —
ступени, и знать их снаружи не нужно. Перечень осей процесса целиком —
[shared/axes.md](../../shared/axes.md).
**Состав не выводится ни из чего: он один и тот же на всякой задаче.** Гейт,
сверка со спекой, разбор кода, триаж; приёмник тем — когда у проекта есть свои
темы. Прежде состав выбирала **метка** `small`/`medium`/`large`, которую считал
отдельный проход по двум осям — размеру и сложности. Метки больше нет, и вместе с
ней ушли разметка, матрица выбора, правило «спорное решается вниз» и доли по
журналу.
**Цикл задачи проверяет корректность и механику, и это его определение.**
Вопрос «делает ли код заказанное и не сломается ли он сам» отвечается против
**записанного** критерия: дельта-спеки, конвенции, инварианты `CLAUDE.md`, вывод
инструментов. Вопрос «то ли это решение» здесь не задаётся вовсе: он стоит
человеку разговора, а место разговора назначено — чекпоинт до кода, где форму
решения одобряет человек, и скилл `av-dev:code-deep-review`, где находки
разбирают по одной.
Отсюда таблица тем — единственная и без вариантов:
<!-- дом: тема-глубина -->
| Тема | Кто закрывает | Против чего и как |
|---|---|---|
| `autotests` | `autotests` | запуск: гейт проекта и логи его шагов |
| `requirements` | `specs` | разбор: дельта-спеки change, сверка в обе стороны |
| `conventions` | `code` | разбор: дома конвенций проекта |
| техника | `code` | разбор: дефект, который сработает сам |
| `security`, `operations`, `architecture` | `code` | **сверка с записанными инвариантами `CLAUDE.md`** — и только |
| тема проекта | `basics` | разбор: дом темы против диффа |
<!-- /дом: тема-глубина -->
**Три темы риска и устройства закрыты узко, и это названо прямо.** Свойство,
которого нет в инвариантах, в цикле не спросит никто: ни сценарием, ни чтением
дома темы. Это самая крупная граница покрытия конвейера, она идёт строкой в
каждом отчёте, и снимает её не прогон задачи, а глубокое ревью области.
Весь процесс с исполнителями — одной схемой:
```mermaid
flowchart TD
propose["opsx:propose — change, дельта-спеки, tasks.md"]
checkpoint(["чекпоинт: форму решения одобряет человек"])
apply["opsx:apply — код, гейт зелёный"]
subgraph code["Ревью кода — состав постоянный"]
cGate["autotests — гейт, источник графа"]
cS["specs — requirements"]
cC["code — conventions, техника<br/>и сверка с инвариантами:<br/>security, operations, architecture"]
cB["basics — только свои темы проекта"]
cT["triage — единственный сток"]
end
propose --> checkpoint --> apply --> cGate
cGate -->|зелёный| cS
cGate -->|зелёный| cC
cGate -->|"зелёный, есть свои темы"| cB
cS --> cT
cC --> cT
cB --> cT
```
**Глубины две, и они не про старательность, а про способ доказательства.**
**Сверка** — открыть дом темы, открыть дифф, сравнить. **Разбор** — построить
сценарий рассуждением, ничего не запуская.
**Третья глубина — доказательство** (прогнать, померить, построить путь) — в
цикле задачи не производится вовсе. Она требует машины и стоит часов, и потому
живёт в скилле `av-dev:code-deep-review`, который идёт по названной области и
время от времени. Проход, которому в плане назначили доказательство, получил план
не от конвейера задачи.
**Необратимое изменение состава не меняет — оно меняет адресата находки.**
Миграция схемы и данных, формат на диске, публичный контракт, имя, разошедшееся
по кодовой базе, — всё, что после мерджа не откатывается обратной правкой. Раньше
это был отрицательный тест метки `small`: такое изменение поднимало метку и
получало лишний проход. Поднимать больше нечего, и правило работает иначе:
находка по необратимому месту помечается `Действие: развилка` и уходит человеку,
а не чинится молча, каким бы мелким ни был дифф. Цена ошибки тут не в размере
правки, а в том, что её не отменить.
**Состав сверяется до коммита — по таблице тем выше.** Она и есть реестр: тема,
кто закрывает, против чего. Это единственная защита от промаха, который уже
случился: пропуск **не отличим от прохода без находок** (гейт зелёный, спеки
сошлись, отчёт выглядит полным), а заметить его мог бы только триаж, который сам
заполняется тем, что ему подали. Непущенное идёт строкой «не запускался» с
причиной, а не отсутствует. Цена молчащего пропуска измерена: семь находок и
отдельная задача на их дозакрытие.
**Сверять теперь дешевле, и это главный выигрыш от снятия метки.** Реестр был
переменным — он приезжал планом разметки и на каждой задаче выглядел иначе;
пропущенную тему приходилось искать сверкой двух списков. Реестр постоянный
сверяется взглядом: против каждой строки таблицы либо отчёт, либо названная
причина, по которой проход не пущен.
## Порядок прогона — граф, а не очередь
Таблица тем отвечает «что и против чего проверяется», порядок — «что кого
ждёт». Ступени остаются единицей **состава**, но порядок задают **не их номера**:
между ступенями 2 и 3 настоящих зависимостей нет — ни один проход не читает вывод
другого, — и очередь между ними была бы платой ни за что.
Рёбер два вида, и они разной природы. Путать их нельзя: первое про
**осмысленность** (на красном гейте проходу с мнением не о чем судить), второе
про **железо**.
| Ребро | Смысл | Между кем |
|---|---|---|
| **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | гейт → все проходы с мнением; все проходы → триаж |
| **конфликт за ресурс** | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» |
**Узла, который считает состав, у графа нет.** Раньше первым узлом каждого
прогона стояла разметка и ребро «разметка → все» шло отсюда; состав постоянный, и
считать его больше нечем и незачем.
```mermaid
flowchart TD
autotests["autotests<br/>(ступень 1, держит машину)"]
specs["specs"]
code["code"]
basics["basics<br/>(только свои темы проекта)"]
triage["triage — единственный сток"]
autotests -->|зелёный| specs
autotests -->|зелёный| code
autotests -->|"зелёный, есть свои темы"| basics
specs --> triage
code --> triage
basics --> triage
```
Читается граф так: **всё, у чего входящие рёбра закрыты, уходит одним
сообщением**. Источник графа — гейт: он один по построению и идёт первым. После
зелёного гейта уходят разом `specs` и `code`, а с ними `basics`, если у проекта
есть свои темы; триаж стартует, когда вернулся последний. Глубина графа — три
шага при любой задаче, и это же его худший случай.
**Схема здесь старше прозы.** Она не иллюстрация к тексту, а сам алгоритм
планировщика; проза ниже объясняет рёбра и называет их цену. Разошлись — прав
граф, а расхождение чинится правкой текста.
**Ребро значит «A закончил раньше, чем B стартовал», и ничего больше.** В обычном
графе задач ребро тянет за собой данные — здесь нет, и это не деталь реализации.
Проход **не видит** находок других проходов, в каком бы порядке их ни запустили.
Вся ценность конвейера держится на разведённости: под всеми ролями одна модель с
одними априорными, и стоит показать ей чужой вывод — она согласится. Согласие
нескольких проходов и так не повышает `confidence` (см. «Честный предел»);
согласие **наведённое** ещё и маскируется под независимое подтверждение.
Единственный, кто получает чужие выводы, — триаж, и это его работа.
### Кто держит машину
Ресурс один и неделимый: **машина** — тесты, поднятый сервис, СУБД, порты, диск.
Проходы, заявившие его, сериализуются между собой на любой ступени; порядок
внутри цепочки произволен.
| Проход | Держит машину | Почему |
|---|---|---|
| `autotests` | да | запускает инструменты проекта — но он источник графа и один по построению |
| `triage` | да | проверяет оракул `major` запуском — но он сток и тоже один |
| `specs`, `code`, `basics`, `rubric` | нет | читают и рассуждают, ничего не исполняют |
**В цикле задачи цепочки за машину нет.** Оба прохода, что её держали —
`adversary` и `ops`, — переехали в скилл `av-dev:code-deep-review`; там правило
действует целиком, и дом его остаётся здесь. Оставшиеся двое машину держат, но
каждый один по построению: один источник графа, другой сток.
**Правило про ресурс, а не про имена.** Раньше здесь стояло именованное
исключение «`adversary` и `ops`»; оно рассыпается, как только проход начнёт
мерить или в проекте появится свой. Два прохода на одной машине соревнуются за
диск, CPU и за саму СУБД и выдают числа, которые не воспроизведутся, — а число,
снятое под конкурентную нагрузку, это находка с испорченным оракулом. Её
опровержение стоит дороже всего выигрыша от параллельности, и она хуже
отсутствующей: выглядит доказанной. Правило выведено из находок, целиком
державшихся на таких замерах; у каждого проекта они свои и лежат в журнале
`docs/review.md`.
Проект вправе пометить «держит машину» и другой проход — в `docs/review.md`,
разделе настройки конвейера. Снимать пометку с перечисленных нельзя.
### Находка «переделать форму» — прогон повторяется целиком
**Барьера стоимости в конвейере нет, и раннего выхода тоже.** Барьер существовал
ради независимой реализации — единственного прохода, чей счёт определялся объёмом
вывода, — и ушёл вместе с ней. Граф плоский, от гейта до триажа: защищать за
барьером нечего, а сериализация не бесплатна — она разводит по очереди то, что
могло идти разом.
Находка «**форму изменения** надо переделывать» ловится триажем, как и любая
другая; дальше правило одно. Находка чинится, и ревью кода запускается **заново с
гейта**, а не «доезжает» остатком по коду, которого через час не станет.
**Пересчитывать перед повтором нечего.** Состав постоянный, и второй прогон
идёт тем же составом, что первый; менять его нельзя даже «раз уж переделываем» —
конвейер, чей состав зависит от истории прогонов, не сверяется ни с чем.
Если прогон всё же остановлен на полпути, незапущенные проходы идут в границы
покрытия строкой «не запускался: прогон остановлен на <проход> из-за <находка>»,
поимённо, а **триаж на половине прогона не запускается**: его отчёт выглядит
полным, потому что агрегирует всё, что ему подали, — это тот же молчащий пропуск,
что и в разделе «Состав прогона».
Находка, которая чинится в пределах существующей формы (`Действие: инлайн`),
прогон не останавливает: дешевле дособрать все находки и починить пачкой, чем
гонять конвейер дважды.
### Линеаризация — когда графа мало
Граф можно вытянуть в одну цепочку. Это отступление, и оно называется в отчёте:
1. **сказал оператор** — «гони линейно». Набора называть не надо: линейный прогон
ничего не портит, он только дольше, и домысливать тут нечего;
2. **машина занята, и знает об этом вызывающий.** Рядом идёт другая задача,
поднят сервис, гоняется дорогая проверка проекта. Сам конвейер занятости
машины не видит — её обязан назвать тот, кто запускает;
3. **разбор самого конвейера** — когда выясняется, почему проход чего-то не
нашёл, порядок и изоляция важнее скорости.
Обратное отступление — **слить цепочку ресурса** (пустить меряющие проходы
разом) — бывает только по прямому слову оператора, и тогда в границы покрытия
идёт строка: какие проходы шли одновременно и что замеры этого прогона как
оракул слабее.
Режим объявляется в отчёте отдельной строкой: **`по графу`** — одним словом,
**`линейно`** — с причиной (какой именно из трёх).
## Прогон без change — сценарий обслуживания
Третий вызывающий конвейера — сценарий обслуживания скилла `av-dev:code-resolve`
(тулчейн и сборка, зависимости, гит-хуки, перенос, чистка). Он приходит **без
change**: у работы, не меняющей поведения, дельта-спек нет по построению.
**Копия.** Дом оси — `shared/axes.md` в репозитории плагина: режим делят конвейер,
сценарий обслуживания и два устава, и ни один из них им не владеет. Правится дом,
а не этот файл.
<!-- копия: режим-прогона из av-dev/shared/axes.md -->
**Прогон ревью идёт в одном из двух режимов, и режим — не глубина.**
- **По change** — обычный прогон цикла задачи: есть дифф и дельта-спеки, состав
постоянный и живёт в конвейере.
- **Без change** — дельта-спек нет по построению, и вместе с ними нет темы
`requirements`. План фиксирован и назван вызывающим; так идёт сценарий
обслуживания.
**Режим правит состав, а не глубину.** Глубина темы стоит в таблице тем
конвейера, одна на все прогоны по change; на прогоне без change её называет план
сценария — иначе проход, чьей темы в плане нет, взял бы глубину наугад.
**Третьего режима у конвейера нет.** Скилл `av-dev:code-deep-review` конвейер не
зовёт вовсе: состав, глубина и вход у него свои, а общее с конвейером — уставы
проходов и контракт находок.
<!-- /копия: режим-прогона -->
**План приходит вызовом и фиксирован сценарием**, а не выводится здесь. **Он же
называет темы и глубину каждого прохода** — таблица тем конвейера описывает
прогон по change, и тема `requirements` в ней есть, а здесь её предмета нет:
<!-- копия: план-обслуживания из av-dev/skills/code-resolve/references/maintain.md -->
| Тема | Дом | Кто закрывает | Глубина и вход | Когда |
| --- | --- | --- | --- | --- |
| `autotests` | `CLAUDE.md`, семантика гейта | `review-autotests` | как обычно: гейт запускается целиком | всегда |
| `operations` | `architecture.*`, раздел эксплуатации | `review-basics` | **сверка**: дом темы против диффа, потолок 2 | всегда |
| `conventions` + технический разбор | `conventions.*` | `review-code` | как в цикле: дом конвенций целиком, потолок 4 конвенционных, у техники потолка нет; сверка с инвариантами `CLAUDE.md` по темам `security`, `operations`, `architecture`, потолок 1 | дифф трогает код, а не только оснастку |
<!-- /копия: план-обслуживания -->
Триаж обязателен и здесь — он единственный сток и единственный, кто сверяет план
с исходом; на его вход подаётся этот план вместо таблицы тем. Тема
`requirements` в плане отсутствует за отсутствием предмета; `security` и
`architecture` закрыты сверкой с записанными инвариантами внутри `code` — ровно
так же, как в цикле задачи. Все три обязаны быть названы в границах покрытия.
Дом плана — сценарий, а не этот скилл: `av-dev:code-resolve`,
`references/maintain.md`, раздел «Ревью — план фиксирован сценарием».
**Правило гейта на таком прогоне работает жёстче обычного.** Правка, которая
трогает сам гейт, проверяется гейтом же — инструмент проверяет себя, — поэтому
сверяется не только цвет, но и состав шагов. Что считается составом, объявляет
проект семантикой гейта в `CLAUDE.md`; не объявил — это строка границ покрытия, а
не догадка прохода.
## Ступень 1 — Автотесты (обязательна)
Агент `review-autotests`, тема `autotests`. Гонит команду гейта из семантики
гейта в `CLAUDE.md` — либо засчитывает прогон, сделанный до ревью, — и
интерпретирует вывод.
**Тема и проход названы одинаково намеренно, а «гейт» осталось именем команды.**
Раньше тема звалась `autotests`, а проход — `gate`: одна сущность под двумя
именами, и вопрос проекта, адресованный одному имени, к другому не приезжал.
Слово «гейт» теперь значит ровно одно — барьер, который проект запускает; тема
шире него ровно на «чего в гейте намеренно нет».
**Гейт, прогнанный до ревью, второй раз не гоняется.** Задача приходит на ревью
с зелёным гейтом: сценарий решения доводит его до зелёного шагом `opsx:apply`,
сценарий обслуживания — своим шагом гейта. Повтор на неизменившемся дереве
вернёт тот же вывод, а стоит он минут — то есть платит ими ни за что.
**Признак один и проверяемый — отпечаток рабочего дерева.** Его снимают дважды:
тот, кто прогнал гейт, сразу после прогона, и проход перед началом работы.
<!-- дом: отпечаток-дерева -->
```sh
{ git rev-parse HEAD; git status --porcelain -uall; git diff HEAD;
git ls-files -o --exclude-standard -z | xargs -0 -r git hash-object; } | sha1sum
```
<!-- /дом: отпечаток-дерева -->
Сводку прошлого прогона, путь к логам шагов и отпечаток проход получает
**заданием** — их передаёт вызывающий скилл. Отпечатки совпали — проход читает
готовую сводку и логи, команду не запускает. Разошлись, отпечатка в задании нет,
логи недоступны — проход гонит гейт сам и ни у кого не спрашивает.
**Отказ здесь безопасен по построению.** Лишний прогон стоит минут, а
засчитанный чужой — красноты, которой никто не увидел. Временный каталог проекта
из отпечатка выпадает сам: `--exclude-standard` отбрасывает игнорируемое, а логи
шагов гейт пишет именно туда. У проекта, держащего временный каталог под git,
отпечатки не совпадут никогда — и он получит честный прогон вместо тихого
засчитывания.
**Переиспользуется команда, а не проход.** Тема `autotests` закрывается целиком:
логи проход читает сам, находки об отсутствующей верификации выдаёт как обычно.
Переиспользование он объявляет строкой сводки и строкой границ покрытия — чем
гейт прогнан, когда и на каком отпечатке. Молчащее переиспользование неотличимо
от собственного прогона, а разница между ними в том, кто видел вывод своими
глазами.
**Пока гейт красный — проходы с мнением не запускаются.** Оркестратор чинит и
перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки
(гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не
блокирует.
Гейт возвращает не только «зелено/красно», но и находки класса **отсутствующая
верификация**: изменённые строки без покрытия, конкурентность без теста с
параллельным доступом, флаки-тест (не ниже `major`), недоступный инструмент.
Шаги выбираются по изменённым файлам: правка документации не гоняет тесты,
линтеры и детектор гонок. Пропуск при этом не молчит — он виден в сводке с
причиной и уезжает в границы покрытия, как и любой другой `SKIP`.
Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу
запрещено списывать такой отказ в мелочь.
## Ступень 2 — Сверка (обязательна)
Два прохода, оба против **записанного** критерия. Машину не держат ни один, ребра
между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со
ступенью 3, если она идёт.
- `review-specs` закрывает тему `requirements`. Критерий взят из **дельта-спек
предлагаемого изменения**, а не из proposal, сообщения коммита или описания
задачи. Сверка двунаправленная; направление `code → spec` важнее.
- `review-code` закрывает тему `conventions` **и делает технический разбор
кода** — это две его половины. Первая ищет дефект, который сработает сам, на
обычном входе: необработанная ветка отказа, пустое значение, граница диапазона,
перепутанный операнд, неосвобождённый ресурс, неверно применённый интерфейс
библиотеки. Вторая сверяет с конвенциями проекта, беря только ту их часть,
которая **не выражается правилом**: механизируемое уже проверила ступень 1.
**Третья его обязанность узкая и постоянная** — сверить дифф с записанными
инвариантами `CLAUDE.md` по темам `security`, `operations` и `architecture`.
Потолок 1 находка на все три темы разом: это не разбор темы, а объявленный
минимум, и в границах покрытия он называется именно так.
**Вход обоих постоянный и полный:** `specs` читает дельта-спеки и затронутые
актуальные спеки, `code` — дом конвенций целиком, до чтения диффа. Прежде вход
сужала метка `small` до дельта-спеки и индекса конвенций; узкий вход ловит
нарушение записанного рода и пропускает то, ради чего конвенцию писали абзацем,
— то есть экономил ровно на той работе, ради которой проход и зовут.
**Потолки у половин `code` раздельные, и это не бюрократия.** Конвенционных
находок больше по построению — родов навигации в разы больше, чем классов
технического дефекта, — и в общем списке они вытесняют техническую половину, чей
пропуск дороже. Раздельный потолок делает вытеснение невозможным: конвенционных
4, инвариантных 1, у технической половины потолка нет.
**Технический разбор — не тема, а обязанность прохода, и он единственный.**
Остальные читают код как материал для своей оптики: `specs` — против требований,
`basics` — против отказов окружения, `architecture` — против устройства. «Здесь
ошибка в логике» не говорит больше никто, и до недавнего времени не говорил
никто вовсе: `code` был проходом только по конвенциям, а дефект ловился разве что
случайно. Это была самая крупная дыра конвейера, и стоила она дороже любой
недосмотренной темы.
Recall темы `conventions` равен длине конвенций проекта — это предел любой
сверки, и снимает его не цикл задачи, а глубокое ревью области.
**Оба прохода на верхней модели, и по одной причине — цене пропуска.** У `specs`
это направление `code → spec`: надо заметить **отсутствие** — тихий фолбэк,
самодеятельный дефолт, проглоченную ошибку. У `code` это пропущенный дефект,
который поедет в прод. Ни то ни другое не оставляет следа ни в отчёте, ни в
границах покрытия; прочие проходы с мнением держат `opus` из-за цены **ложных**
находок, эти двое — из-за цены пропущенных.
**Эта ступень и есть цикл задачи.** С уходом тяжёлых проходов на ней держится всё,
что прогон вообще проверяет по существу: заказанное против сделанного, дефект,
который сработает сам, и конвенции проекта. Отсюда и решение не ставить потолка
технической половине.
## Ступень 3 — Темы проекта (только когда они есть)
Агент `review-basics`. Один проход, машину не держит, ничего не запускает и не
меряет — уходит одним сообщением вместе со ступенью 2, сразу после зелёного
гейта.
**Он приёмник проектных тем, и больше ничей.** Именных проходов конечное число, а
тем столько, сколько заведёт проект: без приёмника открытость списка тем была бы
обещанием без механизма. Темы **ядра** он больше не держит — риск и устройство
закрывает `code` сверкой с инвариантами, а разбор этих тем целиком уехал в
`av-dev:code-deep-review`.
**Запускается тогда и только тогда, когда ему есть что принимать.** Своих тем у
проекта нет — проход не идёт вовсе, и отчёт говорит об этом строкой: «свои темы
проекта не заведены, приёмник не запускался». Это единственное место, где состав
прогона зависит от проекта, и потому оно называется явно.
Глубина одна — **разбор**: построить сценарий рассуждением, дом темы против
диффа; потолок 4 находки. Прежде глубина приезжала планом разметки и на `small`
падала до сверки; плана нет, и падать ей больше неоткуда.
Чего он не делает ни на какой теме — замеров, эксперимента против драйвера,
построенного пути, карты проекта, границы домена. Всё это стоит машины или входа
шире диффа, то есть глубокого ревью области.
## Ступень 4 — Triage (обязательна)
Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.**
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
стартует. Получает сырые выводы всех проходов, `git diff`, режим и **таблицу
тем**; возвращает финальный отчёт.
**На прогоне без change её место занимает план сценария** — см. «Прогон без
change»: сверять исход с планом триаж обязан и там, а другого перечня тем в том
прогоне не существует.
**Таблица тем на входе у триажа — не формальность, а сверка.** Он единственный,
кто видит и то, что заявлено, и то, что пришло: «тем шесть, отчёты покрывают
пять» — находка о самом прогоне, и заметить её больше некому. Раньше он получал
список запущенных проходов и потому мог сверить только состав; теперь сверяет
**темы**, а тема, оставшаяся без отчёта, — это то, чего список проходов никогда
не показывал. Таблица постоянная, и сверка потому дешевле прежней: сравнивать
приходится с одним и тем же реестром, а не с планом, который на каждой задаче
выглядел иначе.
Отсюда же правило, которое иначе выглядит придиркой: **триаж на неполном графе не
запускается**. Прогон, остановленный на полпути находкой «переделать форму», до
стока не доезжает — его отчёт агрегировал бы половину и выглядел бы полным.
Без триажа проходы дают порядка сорока замечаний при единицах существенных.
Потребитель здесь — оркестратор, который **молча реализует** всё, что прочитал:
цена нетриажированного отчёта — не потерянное время человека, а разросшийся от
вкусовщины код.
Порядок: дедупликация по причине → оракул для всего `critical`/`major`
понижение неподтверждённого до гипотезы → отсев вкусовщины → ранжирование по
ущербу × вероятности → потолок 7 пунктов в основном списке.
**Разметку действия ставит он же, и умолчание у неё одно — `инлайн`.** Развилку
получает только то, что инлайном чинить нельзя: находка по необратимому месту и
находка, чья правка меняет дельта-спеки. Остальное чинится молча — см. «Что
происходит с находками дальше».
**Он же собирает строки «отложено в `av-dev:code-deep-review`».** Проход, упёршийся
в предел цикла — нужен замер, нужен прогнанный путь, нужен вход шире диффа, —
пишет об этом в своих границах покрытия; триаж сводит такие строки в одну секцию
отчёта. Без сведения они растворяются по отчётам проходов, и повод позвать
глубокое ревью не накапливается нигде.
## Контракт находок
Единый для всех проходов — [references/finding-contract.md](references/finding-contract.md).
Коротко: заголовок через **последствие**, обязательные поля `Файл`, `Severity`,
`Confidence`, `Оракул`, `Последствие`, `Предложение`, `Найдено проходом`.
`critical` без оракула или построенного пути не существует. Находка без поля
«Последствие» не выводится вовсе.
Каждый проход завершает вывод блоком `## Coverage of this pass`.
## Что происходит с находками дальше
**Умолчание одно, и оно называется прямо: находку чинит агент, молча.** Цикл
задачи устроен так, чтобы человек читал сводку, а не разбирал список замечаний;
всё, что чинится в пределах одобренной формы решения, помечается `Действие:
инлайн`, уходит агенту дословно вместе с оракулом и логированию не подлежит.
Прогон, вернувший человеку десяток вопросов, свою работу не сделал.
Из умолчания два выхода, и оба узкие:
- **`Действие: развилка`** — вопросом с вариантами и ценой каждого туда, где
проект держит вопросы (это знает вызвавший скилл, а не конвейер ревью).
Помечается так **только** то, что инлайном чинить нельзя: находка по
необратимому месту (миграция, формат на диске, публичный контракт) и находка,
чья правка меняет **дельта-спеки** — то есть отменяет одобренное человеком.
Оркестратор при этом не останавливается: он урезает изменение до остатка и
доводит его.
- **урожай** — находка реальная, но не для этого мерджа: отложенный `major`,
развилка, решённая «потом», пачка `nit`. Конвейер отдаёт её **списком** в
отчёте: формулировка, оракул, откуда взялась (какой проход, какой change).
**Задачи из урожая заводятся только по слову человека, и это правило, а не
вежливость.** Спрашивает не конвейер: список уезжает вызывающему и показывается
человеку **одной репликой на весь хвост задачи** — вместе с тем новым, что
предлагает записать синк документации (`av-dev:code-resolve`,
`references/solve.md`, шаг 6). Два вопроса про одно и то же — «что из найденного
переживёт задачу» — стоили бы двух переключений вместо одного. Сказал «заводим» —
зовётся `av-dev:task-track`, у него на этот вход отдельный сценарий «задачи из ревью и
аудита»: свой формат, кластеризация по причине, дедуп против беклога и кладбища.
Не сказал — урожай остаётся строками доклада, и это исход, а не потеря. Прогон,
заводящий задачи сам, наполняет беклог работой, которую никто не выбирал; на
проекте, где очередь работ ведёт один человек, это и есть главная цена лишней
находки. Каталога задач в проекте нет — урожай остаётся списком тем более, и
это говорится строкой.
Остальное не меняется:
- `Promote candidates` — по процедуре [references/promote.md](references/promote.md):
находка → конвенция → правило линтера → **удаление формулировки из конвенций**.
Третий шаг обязателен. **Сама конвенция заводится по слову человека** — той же
репликой, что и задачи из урожая: её строка станет входом каждого следующего
прогона, и из всего, что пишет хвост задачи, она связывает дальше всего.
- Дефект, проскочивший ревью и всплывший позже, идёт в журнал проекта
([references/review-journal.md](references/review-journal.md)) — сразу, не
ретроспективно: теряется именно то, почему дефект не поймали.
- **Отчёт триажа сохраняется вместе с изменением** — `openspec/changes/<id>/review/`.
Он единственное, по чему потом видно, что было найдено и что из этого не
заведено: нулевой урожай при непустом отчёте виден сразу.
**Вместе с изменением он и переезжает:** после `opsx:archive` его адрес —
`openspec/changes/archive/<id>/review/`. Кто ищет отчёт после архивации
(приёмщик на груминге `av-dev:task-groom`, разбор дефекта), смотрит **оба**
пути; «отчёта нет» объявляется, только когда пуст и архивный, иначе самый
дорогой сценарий «состав ревью неизвестен, гоняем заново» срабатывает на
каждой доведённой задаче.
## Честный предел
Модель воспроизводит медиану публичного кода, смещённую к популярному и
туториальному: отсюда тяга к интерфейсам ради интерфейсов, лишним мокам и
конфигурируемости, которую никто не просил. **«Идиоматично» и «распространено» —
разные вещи**; проходы обязаны различать их и опираться на поимённое положение
руководства, а не на ощущение частотности.
Согласие нескольких проходов — **не подтверждение**: это один источник,
высказавшийся несколько раз. Совпадение повышает приоритет, но не `confidence`.
Что недоступно **этому** проекту принципиально — перечисляет «Недоступно
проверке» в `docs/review.*`, по темам, и оба его подраздела целиком уезжают в
границы покрытия. **Тема, у которой нет дома, — тоже граница покрытия**, и она
объявляется на каждом прогоне, а не разово.
Независимо от проекта недоступно:
- поведение внешних систем в их будущих версиях;
- реальный профиль нагрузки и то, что на самом деле лежит в данных;
- завязка внешних потребителей на текущую форму ответа;
- суждение «этой функциональности не должно существовать».
Отдельно и честно: **поимённая сверка с положениями руководств по стилю языка не
задаётся ни одним проходом.** Проход про идиоматичность упразднён, его способные
части переселены (эксперимент против поведения библиотеки и драйвера — в `ops`,
«не изобретаем ли то, что уже есть в библиотеке» — в `architecture`, и оба теперь
живут в `av-dev:code-deep-review`), но различение «идиоматично против
распространено» не спрашивает никто. Класс обратимый — портит форму кода, не
данные, — и его надо признавать в границах покрытия, а не считать проверенным.
**Форму решения в цикле не судит никто, и это сознательное сужение.** Ни ревью
дизайна до кода, ни архитектурного прохода после — обоих сняли, и оба ушли по
одной причине: суждение о форме стоит разговора с человеком, а разговор внутри
задачи растягивает её в часы. Форму одобряет **человек на чекпоинте**, до кода, и
это единственное место цикла, где решение о ней принимается. Всё, что видно
только по написанному коду — второй способ делать уже делаемое, лишний слой,
интерфейс ради мока, — ловится глубоким ревью области, то есть позже и не всегда.
Класс идёт строкой в границы покрытия каждого прогона.
**Запуском в цикле не проверяется ничего сверх гейта, и это на всякой задаче.**
Формулировка «не запускается ничего» была бы короче и была бы ложью: гейт
запускает инструменты проекта, а триаж проверяет оракул `critical`/`major`
запуском — оба идут всегда. Не проверяется **проходом с мнением**: построенный
путь атаки (его надо прогнать), поведение библиотеки и драйвера в вырожденном
случае (достаётся только экспериментом), любое число — время удержания
блокировки, пик кучи, темп роста журнала, стоимость на годовой истории.
**Три темы риска и устройства смотрятся только против записанных инвариантов.**
Отдельная строка, и она обязательна в каждом отчёте: `security`, `operations` и
`architecture` закрывает `code` сверкой с `CLAUDE.md`, потолком 1 находка на все
три. Свойства, которого нет в инвариантах, в цикле не спросит никто. Это не
«глубина ниже» — это **другой дом темы**, куда более узкий, и путать одно с
другим нельзя.
**Ось времени в цикле не смотрит никто.** Обратима ли миграция, что станет с
записями новой версии после отката, как узел ведёт себя через неделю роста —
раньше эти вопросы задавал приёмник тем на метке `medium`, теперь метки нет, а
приёмник держит только свои темы проекта. Взамен работает адресация: находка по
необратимому месту идёт человеку развилкой, а не чинится молча. **Это не
равноценная замена, и подменять одно другим нельзя:** развилка срабатывает,
только если находку кто-то сделал, а по оси времени в цикле её теперь делает
разве что инвариант.
**Решения и измеренные числа проекта прогон не читает вовсе.** `adr.*` и
`research.*` — процессные документы. Отсюда две строки в границы покрытия каждого
прогона: расхождение изменения с записанным решением ловится не здесь, а сверкой
документации; число, на которое опирается находка, обязано быть снято **на этом
прогоне**, иначе находка не поднимается выше гипотезы. Раньше числа брались из
`docs/research/`, и находка выглядела доказанной чужим замером неизвестной
свежести.
**Темы при этом названы все — но закрыты они по-разному, и это надо читать
буквально.** «Тема `security`, глубина сверка» не значит «безопасность
проверена»: значит, что дом темы открыли, дифф посмотрели и сравнили.
**Доказательства в цикле задачи нет, и это самая крупная его граница.** Класс
дефектов, который виден только построенным путём и снятым числом — гонка,
деградация под нагрузкой, исчерпание ресурса, откат бинаря поверх новой схемы, —
здесь не ловится ничем.
Это сознательная сделка, а не пробел в устройстве: тяжёлые проходы оплачивались
на каждой задаче, где запускались, а получались на немногих. Теперь они живут в
`av-dev:code-deep-review` и оплачиваются тогда, когда их решают получить.
Проверяется сделка не рассуждением, а двумя следами: **строками «отложено»** в
отчётах — если по одному месту повторяется один и тот же неснятый замер, глубокий
прогон просрочен, — и **журналом дефектов**: класс, всплывающий после мерджа,
значит, что прогон надо звать чаще.
Так же честно и про упразднённый проход: **«не знаю, чего не знаю» больше
не достаёт никто.** Проход независимой реализации писал свою версию узла, не
открывая существующую, и диффил по решениям — декомпозиция, владение данными,
модель конкурентности, форма решения там, где спека выбора не сделала. Он снят по
решению оператора о **стоимости** — счёт определялся объёмом вывода, и на прогон
он тратил больше всех остальных проходов вместе, — а не по замеру, который
[calibration.md](references/calibration.md) требует перед удалением. Значит и
записывается это как сознательное сужение, а не как «класс оказался пустым».
Класс идёт строкой в границы покрытия каждого прогона — там же, где проект
перечисляет своё в подразделе «перестали проверять сознательно».
Это и есть причина, по которой конвейер готовит ревью, а не заменяет его.
## Ссылки
- [references/project-facts.md](references/project-facts.md) — что нужно проходу
и где это лежит в документах проекта; таблица поразрядной деградации.
- Skill `av-dev:code-deep-review` — глубокое ревью области кода: там живут
`review-adversary`, `review-ops` и `review-architecture`, там же единственное
место процесса, где находка доказывается прогоном и замером, а форма решения
вообще обсуждается.
- Skill `av-dev:canon` — приведение проекта к канону документов.
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
- [references/promote.md](references/promote.md) — промоут находка → конвенция → правило → удаление.
- [references/calibration.md](references/calibration.md) — калибровка инъекцией, вердикты keep/retune/drop.
- [references/review-journal.md](references/review-journal.md) — журнал проскочивших дефектов.