--- name: review-pipeline description: "Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Разметка задачи идёт один раз, после propose: агент review-scope выводит размер и сложность, из их максимума — метка, и раздаёт темы проходам обеих стадий. Метка правит и ревью дизайна (small — только specs; medium — плюс rubric; large — плюс architecture), и ревью кода (small — гейт, спеки, код, триаж; medium — плюс приёмник тем; large — плюс доказательство: враждебные постановки, эксплуатационный постмортем, архитектурный проход на широком входе). Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью) и из task-batch (финальная сверка)." --- # Конвейер ревью Готовит ревью — **не заменяет его**. Потребитель отчёта — оркестратор, который чинит код; человек читает только сводку, развилки и границы покрытия. ## Четыре правила, из которых всё следует Если ситуация не покрыта инструкцией — решай по ним. 0. **Тема первична, проход вторичен.** Ревью проверяет **темы** — набор направлений, который проект объявляет своими документами. Проход это только способ закрыть тему на заданной глубине, и проходы меняются: уезжают в старшую метку, сливаются, упраздняются. Если состав прогона считать списком проходов, то уехавший проход уносит тему с собой **беззвучно** — отчёт честно скажет «`ops` не запускался» и не скажет «эксплуатацию не смотрел никто», а нужно второе. Поэтому прогон описывается таблицей «тема → глубина → кто закрывает», и таблица эта есть в каждом отчёте. 1. **Recall чек-листа равен длине чек-листа.** Проход, устроенный как «проверь пункты 1..N», найдёт ровно перечисленное. Всё неявное — идиомы, форма решения, «так не делают» — неперечислимо по определению: перечислимое уже стало бы конвенцией. Отсюда деление проходов на **applicative** (применяют заданный критерий) и **generative** (сперва порождают критерий или альтернативу, потом сравнивают). Расширять чек-листы бесполезно; неявный слой достают только generative-проходы. 2. **Ценность верификатора = наличие внешнего оракула × разведённость с автором**, а не число ролей. Под всеми ролями одна модель с одними априорными, вход у всех общий: седьмая роль почти не добавляет recall, но линейно удорожает триаж. Иерархия надёжности: детерминированный инструмент > агент, который его **запускает** и интерпретирует вывод > агент с чистым мнением. Максимум работы переносим вниз. 3. **Отчёт без границ покрытия хуже отсутствия отчёта.** «Критичных проблем не обнаружено» потребляет ощущение проверенности, ничего не гарантируя. Секция границ покрытия обязательна и не сокращается — в том числе в докладе человеку. ## Предпосылки Конвейер опирается на внешнюю обвязку и без неё работает не целиком. Проверь это один раз, при установке плагина в проект: - **OpenSpec — жёсткая предпосылка, а не опция.** Ревью дизайна, проход `review-specs` и вызывающий пайплайн задачи завязаны на дельта-спеки (`openspec/changes//specs/*/spec.md`), на актуальные спеки (`openspec/specs/`) и на `openspec validate --strict`. В проекте без OpenSpec шаги, зовущие `opsx:explore` / `opsx:propose` / `opsx:apply` / `opsx:archive`, упадут на «нет такого скилла», а `review-specs` останется без источника требований. **Проект без OpenSpec этим конвейером не проверяется** — подключай OpenSpec, а не понижай прогон: ветка деградации здесь не пишется, потому что непроверенная ветка деградации хуже честного отказа. Заводить руками не надо: `av-dev-pm:init` делает `openspec init` на новом проекте, `canon adopt` — на переводимом, и оба кладут `openspec/config.yaml` канонической формы. - **Документы канона** — см. следующий раздел. - **Проектные копии этих скиллов и агентов удаляются при установке.** Если в проекте уже лежат свои `.claude/skills/review-pipeline`, `.claude/skills/task-pipeline`, `.claude/skills/task-batch` или `.claude/agents/<проект>-review-*.md` — снеси их. Иначе короткое имя разрешится в устаревшую проектную копию, молча и без признаков подмены. По той же причине **скиллы этого плагина зовутся с пространством имён**: `av-dev-pipeline:review-pipeline`, `av-dev-pipeline:task-pipeline`, `av-dev-pipeline:task-batch`. ## Темы, источники и процессные документы Раньше здесь стояло плоское правило «каждый документ проекта — тема ревью». Оно верно ровно наполовину, и потому вредно целиком: паспорт и схему хранилища ревью читает, но темами они не являются, а журнал решений и журнал наблюдений ревью изменения не нужны вовсе. Разметчик, применявший правило буквально, обязан был либо завести фантомные темы и продублировать ими работу настоящих, либо потерять документ молча. **Разрез один и проверяемый — тот же, что в каноне: можно ли по документу сказать «в этом изменении сделано не так»?** | Категория | Что конвейер с ней делает | Кто в ней | |---|---|---| | **тема** | заводит направление проверки и требует исполнителя | `conventions.*`, `security.*`, `architecture.*`, свои документы проекта | | **источник темы** | читает как материал чужой темы, своей не порождает | `passport.*`, `database.*`, `CLAUDE.md`/`AGENTS.md`, `openspec/specs/` | | **процессный документ** | не судит по нему изменение | `docs/tasks/`, `docs/review.*`, `docs/adr.*`, `docs/research.*`, `docs/.pm.json` | **Одна процессная запись всё же читается — `docs/review.*`.** В ней лежит настройка самого конвейера: вопросы по темам, журнал дефектов, типовые узлы, типовые ложноположительные. Проход, читающий её, читает **свою обвязку**, а не критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не открывает никто. Дом канона этой раскладки — `av-dev-pm`, `references/canon.md`, раздел «Три категории документов». Конвейер её **читатель**: категории и имена тем он берёт оттуда и своих не заводит. Отсюда то, ради чего правило и заведено: **`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-pm`, агент `doc-consistency`) на сессии между спринтами. Строка об этом обязательна в границах покрытия каждого прогона. **Своя тема проекта бывает двух происхождений, и обе законны:** документ, который проект положил в `docs/` и которого нет в раскладке канона, — и **тема, названная директивой** `CLAUDE.md`/`AGENTS.md`, у которой документа нет вовсе. У второй дом — сама директива; в остальном она ничем не отличается, и в раздаче идёт туда же. Различать их приходится потому, что условие запуска приёмника тем звучит «есть ли свои темы проекта», и тема без файла в `docs/` иначе не попала бы под это условие никогда. **Проектная тема закрывается `basics`**, при любой метке. Именных проходов конечное число, а тем — сколько заведёт проект; приёмник обязателен, иначе открытость списка была бы обещанием без механизма. Темы **ядра** он держит только на `medium`: на `small` их закрывает `code` сверкой по инвариантам, в `large` — именные проходы. Отсюда правило состава: **`basics` запускается тогда и только тогда, когда ему есть что принимать** — см. «Метки». **Тема без дома — законное состояние и отдельная строка.** «Тема `operations` заявлена, `docs/database.md` нет» читается иначе, чем «не смотрели». Деградация поразрядная: нет дома — падает глубина этой темы, и только её. Что именно проход читает по каждой теме — [references/project-facts.md](references/project-facts.md). Отдельного файла-брифа при этом нет: пути известны, посредник не нужен, а второй дом для тех же фактов разошёлся бы и выглядел актуальным. **Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой и предложи скилл `av-dev-pm:canon`: одна операция на проект против деградации на каждой задаче. Прогон при этом не останавливается. ## Что получает каждый проход Задание собирается **по плану разметки задачи** и состоит из шести вещей: - **его темы** — какие темы он закрывает на этом прогоне, у каждой **дом** (путь и раздел) и **глубина**. Дом передаётся адресом, а не пересказом: проход, получивший проинтерпретированный периметр, не заметит, что интерпретация неверна; - **вопросы по его темам** из `docs/review.*`, если они там есть, — **дословно**. Вопрос привязан к теме, а не к имени прохода, и потому переживает переезд прохода между метками; - **контракт находок** — путь к [references/finding-contract.md](references/finding-contract.md) (в установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/`); - **изменение** — идентификатор change и путь к его дельта-спекам; - **база диффа**; - **метка, его глубина и режим** прогона — чтобы проход знал, что писать в границы покрытия. Чего проход **не** получает ни в каком режиме — выводов других проходов. См. «Порядок прогона». ## Модель по проходу Модель выбирается **по цене ошибки прохода, а не по его роду**. Признак рабочий и проверяемый: находка со ссылкой на записанный источник — строку спеки, цель в манифесте, значение в конфиге — опровергается открытием файла, и дешёвая модель ошибается здесь проверяемо; находка-суждение опровергается рассуждением, а рассуждение стоит триажа или человека. Второй род ошибки — **пропуск**: он не стоит ничего сегодня и не виден вовсе, и проход, у которого дороже пропустить, держится наверху, даже будучи applicative. Модель задана во frontmatter каждого агента, менять её здесь не нужно. | Модель | Цвет | Проходы | Почему | |---|---|---|---| | `sonnet` | green | scope, autotests, ops | вывод перечислим и сверяется механически | | `opus` | yellow | specs, code, basics, adversary, rubric, architecture, triage | дорога ошибка — ложная либо пропущенная | **Цвет charter'а кодирует модель, а не роль прохода.** Это единственное назначение цвета: список агентов читается взглядом, и по нему сразу видно, чем платит прогон. Роль прохода из имени и так понятна, а цвет, розданный по ролям, не отвечает ни на один вопрос, который задают во время прогона. Раскладка живёт здесь и **проверяется механически** — цвет ставится один раз при заведении charter'а, а модель потом двигает калибровка, и разъезжаются они молча. **Моделей две, и верхняя из них — `opus`; выше неё конвейер не платит.** Замер: на первом же прогоне самые ценные находки дали `opus`-проходы — сверка спек дала 13 находок с оракулами, а проход про идиоматичность (впоследствии упразднённый) — три эксперимента против драйвера БД с воспроизведёнными числами. Разницы в пользу модели **дороже** `opus` не обнаружилось ни на одном проходе, а прогон на ней стоил заметно дольше и дороже — значит платить за неё не за что. Четверо держатся наверху не за суждение, а по отдельным причинам, и их стоит знать поимённо: - `triage` — через него проходит всё, что оркестратор реализует **молча**: ложноположительная находка становится кодом, потерянный `critical` — дефектом. Ошибка триажа дороже ошибки любого отдельного прохода. - `specs` — по устройству applicative, но направление `code → spec` требует заметить **отсутствие**: тихий фолбэк, самодеятельный дефолт, проглоченную ошибку. Здесь дорог пропуск, а не ложная находка. - `code` — единственный, кто читает код **как код**. Его пропуск это дефект в проде, и он тоже не оставляет следа ни в отчёте, ни в границах покрытия. По той же причине, что `specs`, и это дороже всего в конвейере: проход идёт на каждой задаче. - `architecture` — запускается только со старшей меткой, на 5–10% задач, потолок в 3 находки делает его дешёвым по выходу, а находка на предложении стоит абзаца против переписывания на готовом коде. Дёшево × высокое плечо. **`scope` внизу, и это не противоречие, хотя его ошибка расходится дальше всех — теперь ещё дальше, чем прежде.** С переездом разметки к `propose` он правит состав **обеих** стадий и не пересматривается после кода: ошибка метки стоит всей задачи, а не одного прогона. Модель он всё же держит нижнюю, и вот почему. Его работа распадается надвое: разнесение документов по категориям и раздача тем **перечислимы** — план сверяется с `ls docs/` за секунду, пропущенный документ виден без рассуждения. Выбор метки — суждение, но с переходом на две оси оно стало **дважды перечислимым**: размер считается по перечню границ задачи и дельта-спекам, сложность отвечается одним проверяемым признаком («можно ли до работы назвать тронутые узлы»). Плюс три независимых корректора: отрицательный тест `small`, правило «спорный случай вниз» и **сигнал о заниженной метке от `code`** — тот идёт при любой метке и видит дифф целиком. Дешёвая модель безопасна ровно потому, что её вывод устроен как список, а не как мнение. **Самая дешёвая модель не используется ни на одном проходе, и это не экономия наоборот.** Дешёвая модель на опиниативном проходе даёт правдоподобные находки, которые триаж обязан опровергать оракулом, — а это самая дорогая операция конвейера. Механизируемая же работа здесь вынесена **ниже** модели: гейт, покрытие диффа, карта проекта — это скрипты проекта, они стоят ноль токенов. Дешёвому проходу просто не осталось работы. Экономия достигается **не понижением модели, а тремя другими рычагами**, и все три применяются к каждому опиниативному проходу, а не к одному избранному. 1. **Непуск.** `large` добавляет доказательство — запуск, замер, построенный путь — и стоит часов; `small` снимает приёмник тем. Что при этом перестаёт проверяться, названо поимённо и идёт в границы покрытия. 2. **Вход.** `basics` идёт на верхней модели, но с узким входом: дифф и его окрестности, без карты проекта. На `small` сужаются и остальные: `specs` читает только дельта-спеку, `code` — только индекс конвенций. 3. **Потолок.** Он есть у каждого опиниативного прохода и напечатан: `basics` — 2 находки на сверке и 4 на разборе; `code` — 3 технических и 2 конвенционных на `small`, 4 конвенционных выше; `specs` — 3 на `small`; `architecture` — 3; триаж — 7 в основном списке. Раньше рычагов было заявлено два, и оба применялись к одному `basics`. Проход без потолка выдаёт столько находок, сколько нашёл поверхностей, — а это ровно тот механизм, из-за которого был снят проход независимой реализации: **счёт определялся объёмом вывода**. Потолок ставится не ради краткости отчёта, а против этого. **Потолок обязан быть объявлен, когда он сработал.** Проход, срезавший находки до потолка, говорит об этом строкой в своих границах покрытия: сколько осталось за срезом и какого рода. Молчащий срез неотличим от «больше не нашлось» — это тот же класс молчащего пропуска, что и непущенный проход. ## Метки **Классификация задачи выдаёт ровно одно значение — метку**: `small`, `medium` или `large`. Это **единственный вход, по которому конвейер выбирает исполнителей**: и на дизайне, и на коде состав читается из неё, а не из класса задачи, не из её типа и не из ощущения важности. Метку ставит `review-scope` при разметке задачи; все проходы получают её в задании и обязаны напечатать в своих границах покрытия. **Метка одна на всю задачу и правит обе стадии ревью** — и дизайна, и кода. У изменения нет двух разных «глубин проверки»: величина, из которой выводится состав, — одна и та же пара «размер × сложность», посчитанная один раз. **Метка не меняет список тем — она меняет их дом и глубину.** Все шесть тем ядра названы при любой метке; разница в том, против чего их смотрят (дом темы или только инварианты) и как (чтением, рассуждением или запуском). Ревью кода: | Тема | `small` | `medium` | `large` | |---|---|---|---| | `requirements` | `specs`, сверка | `specs`, разбор | `specs`, разбор | | `autotests` | `autotests` | `autotests` | `autotests` | | `conventions` | `code`, сверка | `code`, разбор | `code`, разбор | | `architecture` | `code`, сверка по инвариантам | `basics`, разбор | `architecture`, доказательство | | `security` | `code`, сверка по инвариантам | `basics`, разбор | `adversary`, доказательство | | `operations` | `code`, сверка по инвариантам | `basics`, разбор | `ops`, доказательство | | тема проекта | `basics`, сверка | `basics`, разбор | `basics`, разбор | Весь процесс с выбором исполнителей на каждом этапе — одной схемой. **Метка считается один раз, в узле разметки, и дальше только читается:** ```mermaid flowchart TD propose["opsx:propose — change, дельта-спеки, tasks.md"] scope["review-scope — разметка задачи
размер × сложность → МЕТКА"] label{{"метка"}} subgraph design["Ревью дизайна — до кода"] dS["specs — всегда"] dR["+ rubric"] dA["+ architecture
+ вопрос автору о трёх формах"] end apply["opsx:apply — код, гейт зелёный"] subgraph code["Ревью кода — после apply"] cGate["autotests — гейт, источник графа"] cS["specs — requirements"] cC["code — conventions + техника"] cInv["code, третья половина:
security, operations, architecture
против инвариантов CLAUDE.md"] cB["basics — темы ядра + свои темы"] cBown["basics — только свои темы проекта"] cHeavy["adversary · ops · architecture
доказательство"] cT["triage — единственный сток"] end propose --> scope --> label label -->|small| dS label -->|medium| dR label -->|large| dA dR -.-> dS dA -.-> dR dS --> apply dR --> apply dA --> apply apply --> cGate cGate -->|зелёный| cS cGate -->|зелёный| cC label -->|small| cInv label -->|small, есть свои темы| cBown label -->|medium| cB label -->|large| cHeavy label -->|large, есть свои темы| cBown cS --> cT cC --> cT cInv --> cT cB --> cT cBown --> cT cHeavy --> cT ``` Пунктир между проходами дизайна значит «включает предыдущее»: `medium` это `specs` **плюс** `rubric`, `large` — они же плюс `architecture`. Отсюда состав обеих стадий: | Метка | Когда | Ревью дизайна | Ревью кода: стадии | Проходов всего | Доля задач | |---|---|---|---|---|---| | `small` | малое **и** знакомое: багфикс, локальная правка, доки | `specs` | 1, 2, 5 (+3 при своих темах) | **5–6** | **до трети, и меньше, чем `medium`** | | `medium` | **рабочее умолчание**: среднее и знакомое | `specs`, `rubric` | 1, 2, 3, 5 | **7** | **большинство** | | `large` | крупное **или** незнакомое: большой рефакторинг, функциональность, форму которой ещё предстоит нащупать | `specs`, `rubric`, `architecture` | 1, 2, 4, 5 (+3 при своих темах) | **10–11** | **5–10%** | Ревью дизайна разбирается отдельно ниже — оно идёт до кода, у него свой плоский граф и свой сток. Здесь оно стоит в таблице потому, что **метка у обеих стадий общая**, и увидеть цену задачи можно только сложив их. **Разметка в этих числах не считается — её платят один раз на задачу, а не один раз на прогон.** Она ушла из состава ревью кода целиком: `review-scope` идёт после `propose`, до ревью дизайна, и его план обслуживает **обе** стадии. Раньше разметка стояла первой в каждом ревью кода, а перед ревью дизайна вызывающий отвечал на тот же вопрос сам — то есть о размере изменения судили дважды и в одном из двух мест без разведённости с автором. **Три глубины, и они не про старательность, а про способ доказательства.** **Сверка** — открыть дом темы, открыть дифф, сравнить. **Разбор** — построить сценарий рассуждением, ничего не запуская. **Доказательство** — прогнать, померить, построить путь. Только третья требует машины, и только она стоит часов. **Здесь диспетчер и кончается: метка названа — состав читается.** Само правило выбора — две оси, «спорное решается вниз», максимум по поверхности, — а с ним разбор того, чем именно `small` дешевле `medium` и почему доли служат проверкой правила, живут в [references/review-levels.md](references/review-levels.md). Тот файл открывают, когда метку **выбирают, оспаривают или калибруют**; его единственный постоянный читатель — `review-scope`. **Состав сверяется до коммита — по плану разметки задачи, а не по этой таблице.** План и есть реестр: тема, дом, глубина, кто закрывает. Это единственная защита от промаха, который уже случился: пропуск **не отличим от прохода без находок** (гейт зелёный, спеки сошлись, отчёт выглядит полным), а заметить его мог бы только триаж, который сам заполняется тем, что ему подали. Непущенное идёт строкой «не запускался» с причиной, а не отсутствует. Цена молчащего пропуска измерена: семь находок и отдельная задача на их дозакрытие. ## Порядок прогона — граф, а не очередь Метка отвечает «какие темы и на какой глубине», порядок — «что кого ждёт». Стадии остаются единицей **состава**, но порядок задают **не их номера**: между стадиями 2–4 настоящих зависимостей нет — ни один проход не читает вывод другого, — и очередь между ними была бы платой ни за что. Рёбер два вида, и они разной природы. Путать их нельзя: первое про **осмысленность** (без плана задание не определено, на красном гейте опиниативный проход не о чем), второе про **железо**. | Ребро | Смысл | Между кем | |---|---|---| | **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | гейт → все опиниативные; все проходы → триаж | | **конфликт за ресурс** | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» | **План разметки — вход графа, а не его узел.** Он готов до того, как ревью кода началось: разметка идёт один раз на задачу, после `propose`. Раньше она была первым узлом каждого прогона, и ребро «разметка → все» стояло здесь; теперь этого ребра нет, потому что нет и узла. ```mermaid flowchart TD plan[/"план разметки задачи
(готов до ревью кода)"/] autotests["autotests
(стадия 1, держит машину)"] specs["specs"] code["code"] basics["basics
(medium: темы ядра и свои;
small, large: только свои темы проекта)"] adversary["adversary
(large, держит машину)"] ops["ops
(large, держит машину)"] architecture["architecture
(large)"] triage["triage — единственный сток"] plan -.->|задания по темам| autotests autotests -->|зелёный| specs autotests -->|зелёный| code autotests -->|"зелёный, темы по плану"| basics autotests -->|"зелёный, large"| adversary autotests -->|"зелёный, large"| ops autotests -->|"зелёный, large"| architecture adversary -. один ресурс — машина .- ops specs --> triage code --> triage basics --> triage adversary --> triage ops --> triage architecture --> triage ``` Читается граф так: **всё, у чего входящие рёбра закрыты, уходит одним сообщением**. Источник графа — гейт: он один по построению и идёт первым. На `medium` после зелёного гейта уходят разом `specs`, `code` и `basics`, и сразу триаж. На `small` — `specs` и `code`, а `basics` только при своих темах проекта. В `large` вместо тем `basics` идут три тяжёлых: `architecture` и первый из меряющей пары — сразу, второй меряющий — следом за первым, и он же определяет, когда стартует триаж. **Схема здесь старше прозы.** Она не иллюстрация к тексту, а сам алгоритм планировщика; проза ниже объясняет рёбра и называет их цену. Разошлись — прав граф, а расхождение чинится правкой текста. **Ребро значит «A закончил раньше, чем B стартовал», и ничего больше.** В обычном графе задач ребро тянет за собой данные — здесь нет, и это не деталь реализации. Проход **не видит** находок других проходов, в каком бы порядке их ни запустили. Вся ценность конвейера держится на разведённости: под всеми ролями одна модель с одними априорными, и стоит показать ей чужой вывод — она согласится. Согласие нескольких проходов и так не повышает `confidence` (см. «Честный предел»); согласие **наведённое** ещё и маскируется под независимое подтверждение. Единственный, кто получает чужие выводы, — триаж, и это его работа. ### Кто держит машину Ресурс один и неделимый: **машина** — тесты, поднятый сервис, СУБД, порты, диск. Проходы, заявившие его, сериализуются между собой при любой метке и на любой стадии; порядок внутри цепочки произволен. | Проход | Держит машину | Почему | |---|---|---| | `autotests` | да | запускает инструменты проекта — но он источник графа и один по построению | | `adversary` | да | находка есть **построенный путь**: он пишет падающий тест и гоняет его | | `ops` | да | доказывает числами: время удержания блокировки, пик кучи, темп роста журнала | | `triage` | да | проверяет оракул `critical`/`major` запуском — но он сток и тоже один | | `specs`, `code`, `basics`, `architecture`, `rubric`, `scope` | нет | читают и рассуждают, ничего не исполняют | **Правило про ресурс, а не про имена.** Раньше здесь стояло именованное исключение «`adversary` и `ops`»; оно рассыпается, как только проход начнёт мерить или в проекте появится свой. Два прохода на одной машине соревнуются за диск, CPU и за саму СУБД и выдают числа, которые не воспроизведутся, — а число, снятое под конкурентную нагрузку, это находка с испорченным оракулом. Её опровержение стоит дороже всего выигрыша от параллельности, и она хуже отсутствующей: выглядит доказанной. Правило выведено из находок, целиком державшихся на таких замерах; у каждого проекта они свои и лежат в журнале `docs/review.md`. Проект вправе пометить «держит машину» и другой проход — в `docs/review.md`, разделе настройки конвейера. Снимать пометку с перечисленных нельзя. ### Находка «переделать форму» — прогон повторяется целиком **Барьера стоимости в конвейере нет, и раннего выхода тоже.** Барьер существовал ради независимой реализации — единственного прохода, чей счёт определялся объёмом вывода, — и ушёл вместе с ней. Граф при любой метке плоский, от гейта до триажа: защищать за барьером нечего, `architecture` дёшев по выходу (потолок 3 находки), а сериализация не бесплатна — она разводит по очереди то, что могло идти разом. Находка «**форму изменения** надо переделывать» ловится триажем, как и любая другая; дальше правило одно. Находка чинится, и ревью кода запускается **заново с гейта**, а не «доезжает» остатком по коду, которого через час не станет. **Разметка при этом не повторяется — кроме одного случая.** План описывает задачу, а не дифф, и переделка формы внутри той же задачи его не отменяет. Повторить разметку надо тогда, когда правка изменила **дельта-спеки**: план выведен из них, и план, выведенный из отменённых требований, будет уверенно называть не те темы. Если прогон всё же остановлен на полпути, незапущенные проходы идут в границы покрытия строкой «не запускался: прогон остановлен на <проход> из-за <находка>», поимённо, а **триаж на половине прогона не запускается**: его отчёт выглядит полным, потому что агрегирует всё, что ему подали, — это тот же молчащий пропуск, что и в разделе «Метки». Находка, которая чинится в пределах существующей формы (`Действие: инлайн`), прогон не останавливает: дешевле дособрать все находки и починить пачкой, чем гонять конвейер дважды. ### Линеаризация — когда графа мало Граф можно вытянуть в одну цепочку. Это отступление, и оно называется в отчёте: 1. **сказал оператор** — «гони линейно». Набора называть не надо: линейный прогон ничего не портит, он только дольше, и домысливать тут нечего; 2. **машина занята, и знает об этом вызывающий.** Рядом идёт другая задача, поднят сервис, гоняется дорогая проверка проекта. Сам конвейер занятости машины не видит — её обязан назвать тот, кто запускает; так и делает `av-dev-pipeline:task-batch`, когда ведёт задачи параллельно; 3. **разбор самого конвейера** — когда выясняется, почему проход чего-то не нашёл, порядок и изоляция важнее скорости. Обратное отступление — **слить цепочку ресурса** (пустить меряющие проходы разом) — бывает только по прямому слову оператора, и тогда в границы покрытия идёт строка: какие проходы шли одновременно и что замеры этого прогона как оракул слабее. Режим объявляется в отчёте наравне с меткой: **`по графу`** — одним словом, **`линейно`** — с причиной (какой именно из трёх). ## Разметка задачи — один раз, до обеих стадий ревью Агент `review-scope`. Идёт **после `propose`, до ревью дизайна**, и один: до его плана неизвестно ни что проверять, ни на какой метке, ни сколько ревьюверов звать на предложение. **Это не стадия прогона, и в счёт проходов метки она не входит.** Раньше разметка была стадией 0 ревью кода и платилась на каждом прогоне, а перед ревью дизайна тот же вопрос — «крупное или незнакомое?» — задавал сам вызывающий, то есть оркестратор, который только что написал предложение. Одна и та же величина считалась дважды, и один из двух раз — без разведённости с автором. Теперь она считается один раз и обслуживает обе стадии. **Что он читает.** `docs/` на уровне имён и заголовков, `CLAUDE.md` и `AGENTS.md`, `openspec/specs/`, `docs/review.*` — раздел настройки. Плюс **корпус оценки**: пять письменных источников о задаче, из которых выводятся обе оси. | Источник | Размер | Сложность | |---|---|---| | запись задачи, «Затрагивает» | перечень границ, названный до работы | узлы названы поимённо — знакомое | | `proposal.md` | что делаем и зачем | вводит ли новое понятие | | `design.md` | какие узлы в решении | **разбирались ли альтернативы** | | `tasks.md` | число шагов и их разнородность | шаг «разобраться», «выяснить» | | дельта-спеки | сколько capability и требований | `ADDED` целой capability против `MODIFIED` | **Диффа он не читает: кода ещё нет** — и это причина, по которой корпус должен быть широким. Одни дельта-спеки описывают заказанное поведение, но молчат об объёме работы: шесть шагов в двух узлах видны в `tasks.md`, а факт, что форму решения выбирали из нескольких, — только в `design.md`. Оценка по одному источнику это оценка по остатку. **Расхождение источников по объёму разрешается в пользу большего** — не «спорное вниз»: там ничья при равных данных, здесь один источник просто видел больше. Само расхождение при этом идёт доводом за `незнакомое`: если о задаче написано так, что источники не сходятся в объёме, формы решения не знают. Возвращает **план задачи**: размер и сложность с обоснованием, метка как максимум по ним, состав ревью дизайна, список тем с домами и глубинами для ревью кода, разнесение документов по трём категориям и строку про найденные директивы. План уезжает в отчёты обеих стадий целиком и служит границами покрытия. **Он не судит по существу** — ни одной находки об изменении. Его ошибка это пропущенная тема или не та метка, и обе видны: разнесение документов сверяется с `ls docs/` за секунду, а заниженную метку ловит `code` своим сигналом — на любой метке, потому что он идёт всегда. Право у разметчика симметричное: **поднять и понизить метку он может одинаково**, но обоснование обязательно в обоих случаях и всегда — строкой, какой признак по какой оси сработал и по какому факту. **План живёт в контексте прогона задачи и на диск не пишется.** Файл-план был бы четвёртым артефактом рядом с `proposal.md`, `tasks.md` и `design.md`, жил бы дольше задачи и расходился бы с ней молча. Прервали пайплайн — разметка повторяется; это самый дешёвый проход конвейера, и платить за его вечность дороже, чем перезапустить. **Метка, названная до кода, после кода не пересматривается.** Дифф может выйти крупнее ожидания — метка от этого не двинется. Решение сознательное: пересмотр означал бы либо второй запуск разметчика (ровно то, что здесь убрано), либо машинный порог, который на всякой нетипичной задаче срабатывает не туда. Расхождение факта с разметкой ловится **журналом дефектов** в `docs/review.md`, постфактум, и это единственный сигнал — ровно как и для всякой другой ошибки выбора метки. ## Стадия 1 — Автотесты (обязательна при любой метке) Агент `review-autotests`, тема `autotests`. Запускает команду гейта из семантики гейта в `CLAUDE.md` и интерпретирует вывод. **Тема и проход названы одинаково намеренно, а «гейт» осталось именем команды.** Раньше тема звалась `autotests`, а проход — `gate`: одна сущность под двумя именами, и вопрос проекта, адресованный одному имени, к другому не приезжал. Слово «гейт» теперь значит ровно одно — барьер, который проект запускает; тема шире него ровно на «чего в гейте намеренно нет». **Пока гейт красный — опиниативные проходы не запускаются.** Оркестратор чинит и перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки (гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не блокирует. Гейт возвращает не только «зелено/красно», но и находки класса **отсутствующая верификация**: изменённые строки без покрытия, конкурентность без теста с параллельным доступом, флаки-тест (не ниже `major`), недоступный инструмент. Шаги выбираются по изменённым файлам: правка документации не гоняет тесты, линтеры и детектор гонок. Пропуск при этом не молчит — он виден в сводке с причиной и уезжает в границы покрытия, как и любой другой `SKIP`. Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу запрещено списывать такой отказ в мелочь. ## Стадия 2 — Сверка (обязательна при любой метке) Два прохода, оба против **записанного** критерия. Машину не держат ни один, ребра между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со стадией 3 или 4 — той, что в метки. - `review-specs` закрывает тему `requirements`. Критерий взят из **дельта-спек предлагаемого изменения**, а не из proposal, сообщения коммита или описания задачи. Сверка двунаправленная; направление `code → spec` важнее. - `review-code` закрывает тему `conventions` **и делает технический разбор кода** — это две его половины. Первая ищет дефект, который сработает сам, на обычном входе: необработанная ветка отказа, пустое значение, граница диапазона, перепутанный операнд, неосвобождённый ресурс, неверно применённый интерфейс библиотеки. Вторая сверяет с конвенциями проекта, беря только ту их часть, которая **не выражается правилом**: механизируемое уже проверила стадия 1. **На `small` у него есть третья, узкая обязанность** — сверить дифф с записанными инвариантами `CLAUDE.md` по темам `security`, `operations` и `architecture`, потому что с этой меткой `basics` не идёт. Потолок 1 находка на все три темы разом: это не замена приёмнику тем, а объявленный минимум. **Вход обеих половин зависит от метки, и это второй рычаг дешевизны `small`.** На `small` `specs` читает только дельта-спеку, `code` — только **индекс** конвенций: перечень родов и пометки, что уже механизировано. На `medium` и выше оба читают дома целиком. Разница честная: узкий вход ловит нарушение записанного рода и пропускает то, ради чего конвенцию писали абзацем. **Потолки у обоих половин раздельные, и это не бюрократия.** Конвенционных находок больше по построению — родов навигации в разы больше, чем классов технического дефекта, — и в общем списке они вытесняют техническую половину, чей пропуск дороже. Раздельный потолок делает вытеснение невозможным: на `small` это 3 технических и 2 конвенционных, на `medium` и выше потолка нет у первой половины и 4 у второй. **Технический разбор — не тема, а обязанность прохода, и он единственный.** Остальные читают код как материал для своей оптики: `specs` — против требований, `basics` — против отказов окружения, `architecture` — против устройства. «Здесь ошибка в логике» не говорит больше никто, и до недавнего времени не говорил никто вовсе: `code` был проходом только по конвенциям, а дефект ловился разве что случайно. Это была самая крупная дыра конвейера, и стоила она дороже любой недосмотренной темы. Recall темы `conventions` равен длине конвенций проекта — это предел любой сверки, и ровно ради него существуют стадии 3 и 4. **Оба прохода на верхней модели, и по одной причине — цене пропуска.** У `specs` это направление `code → spec`: надо заметить **отсутствие** — тихий фолбэк, самодеятельный дефолт, проглоченную ошибку. У `code` это пропущенный дефект, который поедет в прод. Ни то ни другое не оставляет следа ни в отчёте, ни в границах покрытия; прочие опиниативные проходы держат `opus` из-за цены **ложных** находок, эти двое — из-за цены пропущенных. ## Стадия 3 — Темы (`medium` целиком; `small` и `large` — только свои темы проекта) Агент `review-basics`. Один проход, машину не держит, ничего не запускает и не меряет — уходит одним сообщением вместе со стадией 2, сразу после зелёного гейта. **Он не самостоятельная оптика, а держатель тем, у которых с этой меткой нет своего проходчика.** На `medium` это `security`, `operations` и `architecture`: их именные проходы живут в `large`, а на `small` эти темы смотрятся только против инвариантов внутри `code`. При любой метке, включая `small` и `large`, он же — **приёмник проектных тем**: именных проходов конечное число, а тем столько, сколько заведёт проект. **Он запускается только тогда, когда ему есть что принимать, и это правило одно на все три метки.** На `medium` темы у него есть всегда — три ядра плюс свои. На `small` и `large` — только свои темы проекта; нет таких, и план говорит строкой: на `large` «все темы разобраны именными проходами», на `small` «темы ядра `security`, `operations`, `architecture` закрыты сверкой по инвариантам внутри `code`». Это единственное место, где состав не выводится из одной лишь метки, и потому оно называется в плане явно. Глубина приходит из плана: **сверка** (открыть дом темы, открыть дифф, сравнить; потолок 2 находки) или **разбор** (построить сценарий рассуждением; потолок 4). Обе глубины действуют и на темах ядра, и на проектных: раньше проектная тема разбиралась «так же» независимо от метки, и различие меток на ней не работало вовсе. Чего он не делает ни на какой глубине — замеров, эксперимента против драйвера, построенного пути, карты проекта, границы домена. Всё это стоит машины или входа шире диффа, то есть ровно того, ради чего существует `large`. **Он покрывает миграцию и публичный контракт на `medium`.** Это не побочный эффект, а условие, при котором миграция схемы вообще может не поднимать метка: её шаг гоняет `autotests`, спеку сверяет `specs`, а вопросы «обратима ли» и «что с записями новой версии после отката» задаёт здесь `basics`, темой `operations`. **И ровно поэтому отрицательный тест `small` стал жёстче, а не мягче:** на `small` этого прохода нет, миграция и публичный контракт остаются без единственного взгляда на ось времени — значит изменение, которое не откатывается обратной правкой, на `small` не идёт вовсе, каким бы малым оно ни было. ## Стадия 4 — Доказательство (только `large`) Три прохода, и все три уходят сразу после зелёного гейта, в одном ряду со стадией 2. Каждый берёт свою тему и доводит её до **доказательства**: - `review-adversary`, тема `security` — находка есть **построенный путь**, а не свойство: он пишет падающий тест и гоняет его; - `review-ops`, тема `operations` — постмортем от симптома у владельца сервиса к строке кода, с числами; - `review-architecture`, тема `architecture` — концептуальная целостность на входе шире диффа. **Первые двое помечены «держит машину», поэтому между ними ребро конфликта: они идут цепочкой, а не разом** (правило и его причина — в «Порядок прогона», раздел «Кто держит машину»). Направления у ребра нет: кто первый — неважно. `architecture` машину не держит и ждать ему нечего — он уходит в первой волне. Цепочка не отменяется общим «гони по графу» — она и есть часть графа. Отменяет её только прямое слово оператора про эту пару, и тогда в границы покрытия идёт строка, что числа прогона сняты под соседней нагрузкой. **Эта стадия зарабатывает больше всех остальных вместе — и она же дороже всех остальных вместе.** Измерено на пяти задачах подряд: враждебный проход дал пять из семи выживших находок дозапуска (включая обе верхние); эксплуатационный — единственный, кто нашёл, что откат бинаря поверх новой схемы стартует молча. Оба несут внешний оракул по построению: один обязан путь **прогнать**, второй смотрит ось времени и эксплуатации. Ровно поэтому они и стоят денег: оракул добывается запуском, а запуск — это машина, цепочка и часы. Раньше эта пара стояла в `medium`, то есть на большинстве задач. Стадия переехала в `large` **сознательно и по цене, а не потому, что перестала находить**: она осталась самой ценной, но её ценность оплачивается на каждой задаче, а получается — на немногих. Что из-за этого перестало проверяться на младших метках, названо в «Честном пределе» и обязано идти строкой в границы покрытия каждого прогона `small` и `medium`. Дома тем приходят из плана разметки задачи: `security` — враждебному, `operations` (эксплуатация и хранилище) — эксплуатационному, `architecture` (устройство, граница домена) — архитектурному. Что с чем сшивать и почему — [project-facts.md](references/project-facts.md), раздел «Сшивать обязаны проходы». Без домов стадия вырождается в общие места. **Числа и решения проекта эта стадия больше не читает.** `research.*` и `adr.*` — процессные документы, и прогон их не открывает. Для эксплуатационного прохода это значит, что **число он обязан снять сам** — замером, а не цитатой из чужой записки; для архитектурного — что граница домена берётся из `passport.*`, а не из истории решений. Обе потери названы в «Честном пределе». **Условие стадии и есть условие метки `large`:** изменение крупное **или** незнакомое — любая из двух осей. Разведены они не для красоты: у архитектурного прохода работа появляется от **размера** (трогается несколько слоёв разом или в проекте становится больше сущностей, чем было), у меряющей пары — от **сложности** (форму решения нащупывали по ходу, и потому неизвестно, где она протекает). На малом и знакомом вопрос «не появился ли второй способ» отвечается «нет» до запуска, а построенный путь строить негде. `review-architecture` получает **вход шире диффа**: дерево пакетов с назначением, граф внутренних зависимостей, инвентарь существующих концепций. Команду, которая это готовит, даёт раздел команд `CLAUDE.md`; нет команды — проход собирает карту сам и говорит об этом в границах покрытия. Главный вопрос — концептуальная целостность и **второй способ** делать то, что уже делается. Он же и оправдывает проход: на задаче про пересборку архитектурный проход нашёл, что новый код был **вторым проигрывателем журнала** со своим порядком. Второй обязательный вопрос — **что опытный человек отсюда удалил бы**: слой с единственной реализацией, интерфейс ради мока, незапрошенная конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс секция «дешевле переделать до мерджа». ## Стадия 5 — Triage (обязательна) Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.** Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не стартует. Получает сырые выводы всех проходов, `git diff`, режим и **план разметки задачи**; возвращает финальный отчёт. **План на входе у триажа — не формальность, а сверка.** Он единственный, кто видит и то, что размечено, и то, что пришло: «тем размечено шесть, отчёты покрывают пять» — находка о самом прогоне, и заметить её больше некому. Раньше он получал список запущенных проходов и потому мог сверить только состав; теперь сверяет **темы**, а тема, оставшаяся без отчёта, — это то, чего список проходов никогда не показывал. Отсюда же правило, которое иначе выглядит придиркой: **триаж на неполном графе не запускается**. Прогон, остановленный на полпути находкой «переделать форму», до стока не доезжает — его отчёт агрегировал бы половину и выглядел бы полным. Без триажа проходы дают порядка сорока замечаний при единицах существенных. Потребитель здесь — оркестратор, который **молча реализует** всё, что прочитал: цена нетриажированного отчёта — не потерянное время человека, а разросшийся от вкусовщины код. Порядок: дедупликация по причине → оракул для всего `critical`/`major` → понижение неподтверждённого до гипотезы → отсев вкусовщины → ранжирование по ущербу × вероятности → потолок 7 пунктов в основном списке. ## Ревью дизайна — до кода Запускается на первом чекпоинте ревью (шаг 5 скилла `av-dev-pipeline:task-pipeline`), когда change уже имеет `proposal.md` и дельта-спеки, но кода ещё нет. Разметка задачи к этому моменту уже прошла — она шагом раньше, и метка известна. **Состав растёт метками — теми же, что у ревью кода, и по той же паре осей.** Их называет разметка задачи, а не вызывающий: величина считается один раз и служит обеим стадиям. | Метка | Проходы на предложении | Проходов | |---|---|---| | `small` — малое и знакомое | `specs` | **1** | | `medium` — среднее и знакомое | `specs`, `rubric` | **2** | | `large` — крупное или незнакомое | `specs`, `rubric`, `architecture` + вопрос автору | **3** | - **всегда** — `review-specs` в режиме «дизайн ДО кода». Дельта-спеки сверяются на каждой задаче: это самый дешёвый чекпоинт конвейера, и он ловит то, что на готовом коде уже не чинят; - **со `medium`** — `review-rubric` (фаза 1 без фазы 2: рубрика на задуманный узел становится приёмочными критериями и уезжает в `tasks.md`); - **только в `large`** — `review-architecture` на предложении: можно ли выразить существующими понятиями — **включая конструкции стандартной библиотеки**, — не появляется ли второй способ. Вопрос «не изобретаем ли то, что уже есть в библиотеке» живёт здесь; тогда же задаётся вопрос автору дизайна: **«предложи три формы решения и назови компромисс каждой»** — если ответ показывает, что рассматривалась одна, это находка. **Рубрика съехала на метку вниз, а архитектура осталась наверху — и это не симметричная правка.** Раньше оба прохода включались одним условием, и `medium` получал на предложении ровно один проход, то есть не отличался от `small` вовсе. Разводятся они потому, что зарабатывают на разном: рубрика порождает **свойства узла** и окупается уже на среднем изменении — её выход уезжает приёмочными критериями в `tasks.md` и работает потом на всей задаче; архитектура отвечает на вопрос «не появился ли второй способ», а он на среднем знакомом изменении отвечается «нет» ещё до запуска. Держать её ниже `large` значит платить за предсказуемый ответ на каждой задаче. Причина меток — арифметика, а не экономия на осторожности. Чекпоинт стоит **на каждой задаче**, поэтому каждый проход здесь умножается на число задач, и при мелкой нарезке это самая большая статья конвейера. **Граф этой стадии свой, и он плоский.** Гейта нет — кода ещё нет, запускать нечего; метка уже названа разметкой задачи; машину не держит ни один проход; сток — не триаж, а шаг пайплайна задачи, где замечания отрабатываются правкой спек. Триаж здесь не нужен: находок единицы, и каждая либо правит спеку, либо становится развилкой. ```mermaid flowchart TD plan[/"план разметки задачи:
размер, сложность, метка"/] proposal["предложение: proposal.md + дельта-спеки"] specs["specs (режим «дизайн ДО кода») — всегда"] rubric["rubric, фаза 1 → приёмочные критерии в tasks.md"] arch["architecture на предложении"] author["вопрос автору: три формы решения и компромисс каждой"] fix["шаг пайплайна: правка спек, развилки — вопросом в запись"] plan --> proposal proposal --> specs proposal -->|"метка ≥ medium"| rubric proposal -->|"метка = large"| arch proposal -->|"метка = large"| author specs --> fix rubric --> fix arch --> fix author --> fix ``` Смысл стадии: архитектурная находка на готовом коде стоит переписывания и поэтому игнорируется; та же находка на предложении стоит абзаца обсуждения. `rubric` живёт **только** на этой стадии. Судить код по критерию, под который он писался, — корреляция по построению; те же 8–12 свойств уже лежат приёмочными критериями в `tasks.md`. ## Контракт находок Единый для всех проходов — [references/finding-contract.md](references/finding-contract.md). Коротко: заголовок через **последствие**, обязательные поля `Файл`, `Severity`, `Confidence`, `Оракул`, `Последствие`, `Предложение`, `Найдено проходом`. `critical` без оракула или построенного пути не существует. Находка без поля «Последствие» не выводится вовсе. Каждый проход завершает вывод блоком `## Coverage of this pass`. ## Что происходит с находками дальше - Оркестратор чинит помеченное `Действие: инлайн` и **не логирует мелочь**. - `Действие: развилка` — вопросом с вариантами и ценой каждого туда, где проект держит вопросы (это знает вызвавший пайплайн, а не конвейер). Оркестратор не останавливается: он урезает изменение до остатка и доводит его. - Находка не для этого мерджа, но реальная (отложенный `major`, развилка, решённая «потом»), — не теряется, но **и не заводится здесь**. Конвейер отдаёт её **списком урожая** в отчёте: формулировка, оракул, провенанс (какой проход, какой change). Заведение задач принадлежит тому, кто ведёт задачи проекта, — у него свой формат, своя нарезка и свои правила дублей. Мелочь класса `nit` идёт в урожай одной пачкой, а не записью на находку. - `Promote candidates` — по процедуре [references/promote.md](references/promote.md): находка → конвенция → правило линтера → **удаление формулировки из конвенций**. Третий шаг обязателен. - Дефект, проскочивший ревью и всплывший позже, идёт в журнал проекта ([references/review-journal.md](references/review-journal.md)) — сразу, не ретроспективно: теряется именно то, почему дефект не поймали. - **Отчёт триажа сохраняется вместе с изменением** — `openspec/changes//review/`. Он единственное, по чему потом видно, что было найдено и что из этого не заведено: нулевой урожай при непустом отчёте виден сразу. **Вместе с изменением он и переезжает:** после `opsx:archive` его адрес — `openspec/changes/archive//review/`. Кто ищет отчёт после архивации (батч на финальной сверке, приёмщик на сессии), смотрит **оба** пути; «отчёта нет» объявляется, только когда пуст и архивный, иначе самый дорогой сценарий «состав ревью неизвестен, гоняем заново» срабатывает на каждой доведённой задаче. ## Честный предел Модель воспроизводит медиану публичного кода, смещённую к популярному и туториальному: отсюда тяга к интерфейсам ради интерфейсов, лишним мокам и конфигурируемости, которую никто не просил. **«Идиоматично» и «распространено» — разные вещи**; проходы обязаны различать их и опираться на поимённое положение руководства, а не на ощущение частотности. Согласие нескольких проходов — **не подтверждение**: это один источник, высказавшийся несколько раз. Совпадение повышает приоритет, но не `confidence`. Что недоступно **этому** проекту принципиально — перечисляет «Недоступно проверке» в `docs/review.*`, по темам, и оба его подраздела целиком уезжают в границы покрытия. **Тема, у которой нет дома, — тоже граница покрытия**, и она объявляется планом на каждом прогоне, а не разово. Независимо от проекта недоступно: - поведение внешних систем в их будущих версиях; - реальный профиль нагрузки и то, что на самом деле лежит в данных, — **сверх того, что проход снял замером сам, на этом прогоне**; - завязка внешних потребителей на текущую форму ответа; - суждение «этой функциональности не должно существовать». Отдельно и честно: **поимённая сверка с положениями руководств по стилю языка не задаётся ни одним проходом.** Проход про идиоматичность упразднён, его способные части переселены (эксперимент против поведения библиотеки и драйвера — в `ops`, вопрос 8; «не изобретаем ли то, что уже есть в библиотеке» — в `architecture`, вопрос 1), но различение «идиоматично против распространено» теперь не спрашивает никто. Класс обратимый — портит форму кода, не данные, — и его надо признавать в границах покрытия, а не считать проверенным. **На `small` и `medium` ничего не проверяется запуском сверх гейта.** Это самая крупная граница покрытия конвейера, и она обязана идти строкой в каждом таком прогоне — поимённо, а не общим «метка ниже». Формулировка «не запускается ничего» была бы короче и была бы ложью: гейт запускает инструменты проекта, а триаж проверяет оракул `critical`/`major` запуском — оба идут при любой метке. Не проверяется **опиниативным** проходом: построенный путь атаки (его надо прогнать), поведение библиотеки и драйвера в вырожденном случае (достаётся только экспериментом), любое число — время удержания блокировки, пик кучи, темп роста журнала, стоимость на годовой истории. `basics` задаёт часть тех же вопросов **чтением**, и его ответы поэтому слабее: он формулирует условиями, оракула не приносит и выше гипотезы находку не поднимает — кроме той, что опирается на инвариант `CLAUDE.md`. **На `small` три темы ядра смотрятся только против записанных инвариантов.** Отдельная строка, и она обязательна на каждом прогоне `small`: `security`, `operations` и `architecture` закрывает не приёмник тем, а `code` сверкой с `CLAUDE.md`, потолком 1 находка на все три. Свойства, которого нет в инвариантах, с этой меткой не спросит никто. Это не «глубина ниже» — это **другой дом темы**, куда более узкий, и путать одно с другим нельзя. **Решения и измеренные числа проекта прогон не читает вовсе.** `adr.*` и `research.*` — процессные документы. Отсюда две строки в границы покрытия каждого прогона: расхождение изменения с записанным решением ловится не здесь, а сверкой документации; число, на которое опирается находка, обязано быть снято **на этом прогоне**, иначе находка не поднимается выше гипотезы. Раньше числа брались из `docs/research/`, и находка выглядела доказанной чужим замером неизвестной свежести. **Темы при этом названы все — но закрыты они по-разному, и это надо читать буквально.** «Тема `security`, глубина сверка» не значит «безопасность проверена»: значит, что дом темы открыли, дифф посмотрели и сравнили. Между сверкой и доказательством лежит весь класс дефектов, который виден только построенным путём, — и он проверяется на 5–10% задач. Это сознательная сделка, а не пробел в устройстве: цес меткой `large` платится на каждой задаче, а окупается на немногих. Проверяется сделка не рассуждением, а журналом дефектов: если класс, который ловят только меряющие проходы, начал всплывать после мерджа — метку выбирают слишком низко. Так же честно и про упразднённый проход: **«не знаю, чего не знаю» больше не достаёт никто.** Проход независимой реализации писал свою версию узла, не открывая существующую, и диффил по решениям — декомпозиция, владение данными, модель конкурентности, форма решения там, где спека выбора не сделала. Он снят по решению оператора о **стоимости** — счёт определялся объёмом вывода, и на прогон он тратил больше всех остальных проходов вместе, — а не по замеру, который [calibration.md](references/calibration.md) требует перед удалением. Значит и записывается это как сознательное сужение, а не как «класс оказался пустым»: остаток независимого взгляда даёт ревью дизайна (код пишется под его находки) и `architecture` (второй способ, лишние слои), но **альтернативной реализации, с которой можно сдиффить решения, у конвейера теперь нет**. Класс идёт строкой в границы покрытия каждого прогона — там же, где проект перечисляет своё в подразделе «перестали проверять сознательно». Это и есть причина, по которой конвейер готовит ревью, а не заменяет его. ## Ссылки - [references/project-facts.md](references/project-facts.md) — что нужно проходу и где это лежит в документах проекта; таблица поразрядной деградации. - [references/review-levels.md](references/review-levels.md) — дом правила выбора метки: две оси, спорное вниз, чем `small` дешевле, доли как проверка правила. - Skill `av-dev-pm: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) — журнал проскочивших дефектов.