Files
dev-skills/av-dev/skills/code-review/SKILL.md
T
av daf9f8b824 ревью: лёгкий проход proof в цикле, тяжёлые — в code-deep-review
В цикле задачи темы security и operations закрывает один лёгкий проход
review-proof: чтением и рассуждением, без запуска, потолки раздельные. Машину
он не держит, поэтому идёт в общем залпе — цепочки за ресурс в обычном прогоне
не осталось. Тяжёлая пара adversary и ops переехала в новый скилл
code-deep-review: вход — названная область кода, глубина постоянная, исход —
разбор с человеком и задачи через task-track. Вход глубокому прогону копит сам
цикл строками «отложено». Журнал — тема 76.
2026-08-23 15:55:32 +03:00

104 KiB
Raw Blame History

name, description
name description
code-review Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Разметка задачи идёт один раз, после apply: агент review-scope выводит размер по диффу и сложность по форме решения, из их максимума — метка, и раздаёт темы проходам. Метка правит состав ревью кода: small — гейт, спеки, код, триаж; medium — плюс приёмник тем; large — плюс review-proof (security и operations разом, чтением и рассуждением) и архитектурный проход на широком входе. Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает проходы с мнением, триаж — единственный сток; проходы с пометкой «держит машину» идут цепочкой, но в обычном прогоне таких нет. Тяжёлые проходы — adversary и ops — переехали в скилл av-dev:code-deep-review, который идёт по области кода и время от времени. Проектная специфика приходит из документов канона проекта. Вызывается из скилла av-dev:code-resolve после apply. Второй вызов идёт от сценария обслуживания: без change и без метки, фиксированным планом (autotests, operations, плюс conventions, если тронут код), разметчик при этом не запускается.

Конвейер ревью

Готовит ревью — не заменяет его. Потребитель отчёта — оркестратор, который чинит код; человек читает только сводку, развилки и границы покрытия.

Четыре правила, из которых всё следует

Если ситуация не покрыта инструкцией — решай по ним.

  1. Тема первична, проход вторичен. Ревью проверяет темы — набор направлений, который проект объявляет своими документами. Проход это только способ закрыть тему на заданной глубине, и проходы меняются: уезжают в старшую метку, сливаются, упраздняются. Если состав прогона считать списком проходов, то уехавший проход уносит тему с собой беззвучно — отчёт честно скажет «ops не запускался» и не скажет «эксплуатацию не смотрел никто», а нужно второе. Проверено на живом переезде: ops ушёл в av-dev:code-deep-review, а тема operations осталась в конвейере и досталась proof. Поэтому прогон описывается таблицей «тема → глубина → кто закрывает», и таблица эта есть в каждом отчёте.

  2. Recall чек-листа равен длине чек-листа. Проход, устроенный как «проверь пункты 1..N», найдёт ровно перечисленное. Всё неявное — идиомы, форма решения, «так не делают» — неперечислимо по определению: перечислимое уже стало бы конвенцией. Отсюда деление проходов на applicative (применяют заданный критерий) и generative (сперва порождают критерий или альтернативу, потом сравнивают). Расширять чек-листы бесполезно; неявный слой достают только generative-проходы.

  3. Ценность верификатора = наличие внешнего оракула × разведённость с автором, а не число ролей. Под всеми ролями одна модель с одними априорными, вход у всех общий: седьмая роль почти не добавляет recall, но линейно удорожает триаж. Иерархия надёжности: детерминированный инструмент > агент, который его запускает и интерпретирует вывод > агент с чистым мнением. Максимум работы переносим вниз.

  4. Отчёт без границ покрытия хуже отсутствия отчёта. «Критичных проблем не обнаружено» потребляет ощущение проверенности, ничего не гарантируя. Секция границ покрытия обязательна и не сокращается — в том числе в докладе человеку.

Предпосылки

Конвейер опирается на внешнюю обвязку и без неё работает не целиком. Проверь это один раз, при установке плагина в проект:

  • 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».
  • Документы канона — см. следующий раздел.
  • Проектные копии этих скиллов и агентов удаляются при установке.

Голые имена в .claude/skills/: resolve, review, а у проектов прошлого поколения ещё task-pipeline, review-pipeline, task-batch. С префиксом проекта: <проект>-task-pipeline, <проект>-review-pipeline. Агенты: .claude/agents/<проект>-review-*.md.

Две копии одного скилла расходятся, и побеждает та, что короче названа: короткое имя разрешится в устаревшую проектную копию молча и без признаков подмены.

Чего может не быть

Копия. Дом правила — 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, при любой метке. Именных проходов конечное число, а тем — сколько заведёт проект; приёмник обязателен, иначе открытость списка была бы обещанием без механизма. Темы ядра он держит только на medium: на small их закрывает code сверкой по инвариантам, в large — именные проходы. Отсюда правило состава: basics запускается тогда и только тогда, когда ему есть что принимать — см. «Метки».

Тема без дома — законное состояние и отдельная строка. «Тема operations заявлена, docs/database.md нет» читается иначе, чем «не смотрели». Деградация поразрядная: нет дома — падает глубина этой темы, и только её.

Что именно проход читает по каждой теме — references/project-facts.md. Отдельного файла-брифа при этом нет: пути известны, посредник не нужен, а второй дом для тех же фактов разошёлся бы и выглядел актуальным.

Документов канона нет вовсе — проект не приведён к канону. Скажи это строкой и предложи скилл av-dev:canon: одна операция на проект против деградации на каждой задаче. Прогон при этом не останавливается.

Что получает каждый проход

Задание собирается по плану разметки задачи и состоит из шести вещей:

  • его темы — какие темы он закрывает на этом прогоне, у каждой дом (путь и раздел) и глубина. Дом передаётся адресом, а не пересказом: проход, получивший проинтерпретированный периметр, не заметит, что интерпретация неверна;
  • вопросы по его темам из docs/review.*, если они там есть, — дословно. Вопрос привязан к теме, а не к имени прохода, и потому переживает переезд прохода между метками;
  • контракт находок — путь к references/finding-contract.md (в установленном плагине — ${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/);
  • изменение — идентификатор change и путь к его дельта-спекам;
  • база диффа;
  • метка, его глубина и режим прогона — чтобы проход знал, что писать в границы покрытия.

Ступень 1 получает сверх этого исход гейта, прогнанного до ревью — сводку, путь к логам шагов и отпечаток дерева, — если вызывающий скилл его дал. Зачем и что происходит при расхождении — «Ступень 1 — Автотесты».

Чего проход не получает ни в каком режиме — выводов других проходов. См. «Порядок прогона».

Модель по проходу

Модель выбирается по цене ошибки прохода, а не по его роду. Признак рабочий и проверяемый: находка со ссылкой на записанный источник — строку спеки, цель в манифесте, значение в конфиге — опровергается открытием файла, и дешёвая модель ошибается здесь проверяемо; находка-суждение опровергается рассуждением, а рассуждение стоит триажа или человека. Второй род ошибки — пропуск: он не стоит ничего сегодня и не виден вовсе, и проход, у которого дороже пропустить, держится наверху, даже будучи applicative.

Модель задана во frontmatter каждого агента, менять её здесь не нужно.

Модель Цвет Проходы Почему
sonnet green scope, autotests, ops вывод перечислим и сверяется механически
opus yellow specs, code, basics, proof, rubric, architecture, triage дорога ошибка — ложная либо пропущенная

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

Цвет charter'а кодирует модель, а не роль прохода. Это единственное назначение цвета: список агентов читается взглядом, и по нему сразу видно, чем платит прогон. Роль прохода из имени и так понятна, а цвет, розданный по ролям, не отвечает ни на один вопрос, который задают во время прогона. Раскладка живёт здесь и проверяется механически — цвет ставится один раз при заведении charter'а, а модель потом двигает калибровка, и разъезжаются они молча.

Моделей две, и верхняя из них — opus; выше неё конвейер не платит. Замер: на первом же прогоне самые ценные находки дали opus-проходы — сверка спек дала 13 находок с оракулами, а проход про идиоматичность (впоследствии упразднённый) — три эксперимента против драйвера БД с воспроизведёнными числами. Разницы в пользу модели дороже opus не обнаружилось ни на одном проходе, а прогон на ней стоил заметно дольше и дороже — значит платить за неё не за что.

Четверо держатся наверху не за суждение, а по отдельным причинам, и их стоит знать поимённо:

  • triage — через него проходит всё, что оркестратор реализует молча: ложноположительная находка становится кодом, потерянный critical — дефектом. Ошибка триажа дороже ошибки любого отдельного прохода.
  • specs — по устройству applicative, но направление code → spec требует заметить отсутствие: тихий фолбэк, самодеятельный дефолт, проглоченную ошибку. Здесь дорог пропуск, а не ложная находка.
  • code — единственный, кто читает код как код. Его пропуск это дефект в проде, и он тоже не оставляет следа ни в отчёте, ни в границах покрытия. По той же причине, что specs, и это дороже всего в конвейере: проход идёт на каждой задаче.
  • architecture — запускается только со старшей меткой, на 5–10% задач, и потолок в 3 находки делает его дешёвым по выходу. Его находка дороже прочих по последствиям: второй способ делать то, что уже делается, переписыванием не чинится, а живёт в кодовой базе годами. Дёшево × высокое плечо.

scope внизу, и это не противоречие, хотя его ошибка расходится дальше всех. Он правит состав всего прогона и внутри прогона не пересматривается: ошибка метки стоит всей проверки задачи, а не одного прохода. Модель он всё же держит нижнюю, и вот почему. Его работа распадается надвое: разнесение документов по категориям и раздача тем перечислимы — план сверяется с 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. Проход без потолка выдаёт столько находок, сколько нашёл поверхностей, — а это ровно тот механизм, из-за которого был снят проход независимой реализации: счёт определялся объёмом вывода. Потолок ставится не ради краткости отчёта, а против этого.

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

Метки

Ступени нумерованы и наружу не выходят. Прогон ревью один, и зовёт его av-dev:code-resolve после того, как код написан; членение внутри прогона — ступени, и знать их снаружи не нужно. Перечень осей процесса целиком — shared/axes.md.

Классификация задачи выдаёт ровно одно значение — метку: 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, разбор proof, разбор
operations code, сверка по инвариантам basics, разбор proof, разбор
тема проекта basics, сверка basics, разбор basics, разбор

Весь процесс с выбором исполнителей на каждом этапе — одной схемой. Метка считается один раз, в узле разметки, и дальше только читается:

flowchart TD
    propose["opsx:propose — change, дельта-спеки, tasks.md"]
    checkpoint(["чекпоинт: объяснение человеку"])
    apply["opsx:apply — код, гейт зелёный"]
    scope["review-scope — разметка по диффу<br/>размер × сложность → МЕТКА"]
    label{{"метка"}}

    subgraph code["Ревью кода"]
        cGate["autotests — гейт, источник графа"]
        cS["specs — requirements"]
        cC["code — conventions + техника"]
        cInv["code, третья половина:<br/>security, operations, architecture<br/>против инвариантов CLAUDE.md"]
        cB["basics — темы ядра + свои темы"]
        cBown["basics — только свои темы проекта"]
        cHeavy["proof (security + operations)<br/>· architecture"]
        cT["triage — единственный сток"]
    end

    propose --> checkpoint --> apply --> scope --> label

    label -->|любая метка| 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

Отсюда состав прогона:

Метка Когда Ступени Проходов Доля задач
small малое и знакомое: багфикс, локальная правка, доки 1, 2, 5 (+3 при своих темах) 45 до трети, и меньше, чем medium
medium рабочее умолчание: среднее и знакомое 1, 2, 3, 5 5 большинство
large крупное или незнакомое: большой рефакторинг, функциональность, форму которой ещё предстоит нащупать 1, 2, 4, 5 (+3 при своих темах) 78 510%

Разметка в этих числах не считается — её платят один раз на задачу, а не один раз на прогон. Она ушла из состава прогона целиком: review-scope идёт после apply, до первой ступени. Раньше разметка стояла первой в каждом ревью кода и повторялась при каждом перезапуске прогона.

Глубины две, и они не про старательность, а про способ доказательства. Сверка — открыть дом темы, открыть дифф, сравнить. Разбор — построить сценарий рассуждением, ничего не запуская.

Третья глубина — доказательство (прогнать, померить, построить путь) — в цикле задачи не производится вовсе. Она требует машины и стоит часов, и потому живёт в скилле av-dev:code-deep-review, который идёт по названной области и время от времени. Проход, которому в плане назначили доказательство, получил план не от конвейера задачи.

Здесь диспетчер и кончается: метка названа — состав читается. Само правило выбора — две оси, «спорное решается вниз», максимум по поверхности, — а с ним разбор того, чем именно small дешевле medium и почему доли служат проверкой правила, живут в references/review-levels.md. Тот файл открывают, когда метку выбирают, оспаривают или калибруют; его единственный постоянный читатель — review-scope.

Состав сверяется до коммита — по плану разметки задачи, а не по этой таблице. План и есть реестр: тема, дом, глубина, кто закрывает. Это единственная защита от промаха, который уже случился: пропуск не отличим от прохода без находок (гейт зелёный, спеки сошлись, отчёт выглядит полным), а заметить его мог бы только триаж, который сам заполняется тем, что ему подали. Непущенное идёт строкой «не запускался» с причиной, а не отсутствует. Цена молчащего пропуска измерена: семь находок и отдельная задача на их дозакрытие.

Порядок прогона — граф, а не очередь

Метка отвечает «какие темы и на какой глубине», порядок — «что кого ждёт». Ступени остаются единицей состава, но порядок задают не их номера: между ступенями 2–4 настоящих зависимостей нет — ни один проход не читает вывод другого, — и очередь между ними была бы платой ни за что.

Рёбер два вида, и они разной природы. Путать их нельзя: первое про осмысленность (без плана задание не определено, а на красном гейте проходу с мнением не о чем судить), второе про железо.

Ребро Смысл Между кем
зависимость B не стартует, пока A не закончил, потому что без A задание B не определено гейт → все проходы с мнением; все проходы → триаж
конфликт за ресурс A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет проходы, помеченные «держит машину»

План разметки — вход графа, а не его узел. Он готов до того, как прогон начался: разметка идёт один раз на задачу, сразу после apply. Раньше она была первым узлом каждого прогона, и ребро «разметка → все» стояло здесь; теперь этого ребра нет, потому что нет и узла — перезапуск прогона разметку не повторяет.

flowchart TD
    plan[/"план разметки задачи<br/>(готов до ревью кода)"/]
    autotests["autotests<br/>(ступень 1, держит машину)"]
    specs["specs"]
    code["code"]
    basics["basics<br/>(medium: темы ядра и свои;<br/>small, large: только свои темы проекта)"]
    proof["proof<br/>(large: security + operations)"]
    architecture["architecture<br/>(large)"]
    triage["triage — единственный сток"]

    plan -.->|задания по темам| autotests
    autotests -->|зелёный| specs
    autotests -->|зелёный| code
    autotests -->|"зелёный, темы по плану"| basics
    autotests -->|"зелёный, large"| proof
    autotests -->|"зелёный, large"| architecture
    specs --> triage
    code --> triage
    basics --> triage
    proof --> triage
    architecture --> triage

Читается граф так: всё, у чего входящие рёбра закрыты, уходит одним сообщением. Источник графа — гейт: он один по построению и идёт первым. На medium после зелёного гейта уходят разом specs, code и basics, и сразу триаж. На smallspecs и code, а basics только при своих темах проекта. В large вместо тем basics уходят разом proof и architecture — ждать им нечего, машину не держит ни один, — и триаж стартует, когда вернулся последний.

Схема здесь старше прозы. Она не иллюстрация к тексту, а сам алгоритм планировщика; проза ниже объясняет рёбра и называет их цену. Разошлись — прав граф, а расхождение чинится правкой текста.

Ребро значит «A закончил раньше, чем B стартовал», и ничего больше. В обычном графе задач ребро тянет за собой данные — здесь нет, и это не деталь реализации. Проход не видит находок других проходов, в каком бы порядке их ни запустили. Вся ценность конвейера держится на разведённости: под всеми ролями одна модель с одними априорными, и стоит показать ей чужой вывод — она согласится. Согласие нескольких проходов и так не повышает confidence (см. «Честный предел»); согласие наведённое ещё и маскируется под независимое подтверждение. Единственный, кто получает чужие выводы, — триаж, и это его работа.

Кто держит машину

Ресурс один и неделимый: машина — тесты, поднятый сервис, СУБД, порты, диск. Проходы, заявившие его, сериализуются между собой при любой метке и на любой ступени; порядок внутри цепочки произволен.

Проход Держит машину Почему
autotests да запускает инструменты проекта — но он источник графа и один по построению
triage да проверяет оракул major запуском — но он сток и тоже один
specs, code, basics, proof, architecture, rubric, scope нет читают и рассуждают, ничего не исполняют

В обычном прогоне цепочки за машину нет. Оба прохода, что её держали — adversary и ops, — переехали в скилл av-dev:code-deep-review; там правило действует целиком, и дом его остаётся здесь. Оставшиеся двое машину держат, но каждый один по построению: один источник графа, другой сток.

Правило про ресурс, а не про имена. Раньше здесь стояло именованное исключение «adversary и ops»; оно рассыпается, как только проход начнёт мерить или в проекте появится свой. Два прохода на одной машине соревнуются за диск, CPU и за саму СУБД и выдают числа, которые не воспроизведутся, — а число, снятое под конкурентную нагрузку, это находка с испорченным оракулом. Её опровержение стоит дороже всего выигрыша от параллельности, и она хуже отсутствующей: выглядит доказанной. Правило выведено из находок, целиком державшихся на таких замерах; у каждого проекта они свои и лежат в журнале docs/review.md.

Проект вправе пометить «держит машину» и другой проход — в docs/review.md, разделе настройки конвейера. Снимать пометку с перечисленных нельзя.

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

Барьера стоимости в конвейере нет, и раннего выхода тоже. Барьер существовал ради независимой реализации — единственного прохода, чей счёт определялся объёмом вывода, — и ушёл вместе с ней. Граф при любой метке плоский, от гейта до триажа: защищать за барьером нечего, architecture дёшев по выходу (потолок 3 находки), а сериализация не бесплатна — она разводит по очереди то, что могло идти разом.

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

Разметка при этом не повторяется — кроме одного случая. План описывает задачу, а не дифф, и переделка формы внутри той же задачи его не отменяет. Повторить разметку надо тогда, когда правка изменила дельта-спеки: план выведен из них, и план, выведенный из отменённых требований, будет уверенно называть не те темы. Если прогон всё же остановлен на полпути, незапущенные проходы идут в границы покрытия строкой «не запускался: прогон остановлен на <проход> из-за <находка>», поимённо, а триаж на половине прогона не запускается: его отчёт выглядит полным, потому что агрегирует всё, что ему подали, — это тот же молчащий пропуск, что и в разделе «Метки».

Находка, которая чинится в пределах существующей формы (Действие: инлайн), прогон не останавливает: дешевле дособрать все находки и починить пачкой, чем гонять конвейер дважды.

Линеаризация — когда графа мало

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

  1. сказал оператор — «гони линейно». Набора называть не надо: линейный прогон ничего не портит, он только дольше, и домысливать тут нечего;
  2. машина занята, и знает об этом вызывающий. Рядом идёт другая задача, поднят сервис, гоняется дорогая проверка проекта. Сам конвейер занятости машины не видит — её обязан назвать тот, кто запускает;
  3. разбор самого конвейера — когда выясняется, почему проход чего-то не нашёл, порядок и изоляция важнее скорости.

Обратное отступление — слить цепочку ресурса (пустить меряющие проходы разом) — бывает только по прямому слову оператора, и тогда в границы покрытия идёт строка: какие проходы шли одновременно и что замеры этого прогона как оракул слабее.

Режим объявляется в отчёте наравне с меткой: по графу — одним словом, линейно — с причиной (какой именно из трёх).

Разметка задачи — один раз, после кода

Агент review-scope. Идёт после apply, до первой ступени, и один: до его плана неизвестно ни что проверять, ни на какой метке, ни сколько проходов звать.

Это не ступень прогона, и в счёт проходов метки она не входит. Она платится один раз на задачу: перезапуск прогона по находке «переделать форму» её не повторяет, потому что план описывает задачу, а не дифф.

Что он читает. 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, постфактум, и это единственный сигнал — ровно как и для всякой другой ошибки метки.

Прогон без change — сценарий обслуживания

Третий вызывающий конвейера — сценарий обслуживания скилла av-dev:code-resolve (тулчейн и сборка, зависимости, гит-хуки, перенос, чистка). Он приходит без change: у работы, не меняющей поведения, дельта-спек нет по построению.

Копия. Дом оси — shared/axes.md в репозитории плагина: режим делят конвейер, сценарий обслуживания и два устава, и ни один из них им не владеет. Правится дом, а не этот файл.

Прогон ревью идёт в одном из двух режимов, и режим — не глубина.

  • С меткой — обычный прогон по change: разметку сделал review-scope, состав прогона выведен из метки.
  • Без метки — размечать нечего, план фиксирован и назван вызывающим, разметчик не запускается вовсе. Так идут двое: сценарий обслуживания, у которого нет change, и скилл av-dev:code-deep-review, у которого нет задачи — он смотрит названную область кода.

Без метки — не то же самое, что small. small — это суждение о размере и сложности, снятое с изменения; отсутствие метки — утверждение, что снимать её не с чего. Проход, подставивший себе small там, где метки нет, вывел бы глубину из ничего.

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

Метка на таком прогоне не назначается, и разметчик не зовётся. Обе его оси здесь не определены: размер он выводит из proposal.md, design.md, tasks.md и дельта-спек, а сложность — из формы решения, которая у обслуживания либо известна заранее, либо задача туда не попала (незнакомое уходит в разведку). Разметчик без своего корпуса вернул бы величину, выведенную из ничего, — и это хуже отсутствующей метки, потому что выглядит измеренным.

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

Тема Дом Кто закрывает Глубина и вход Когда
autotests CLAUDE.md, семантика гейта review-autotests как обычно: гейт запускается целиком всегда
operations architecture.*, раздел эксплуатации review-basics сверка: дом темы против диффа, потолок 2 всегда
conventions + технический разбор conventions.* review-code вход small: только индекс конвенций; потолки 3 технических и 2 конвенционных; третья половина включена — сверка с инвариантами CLAUDE.md, потолок 1 дифф трогает код, а не только оснастку

Триаж обязателен и здесь — он единственный сток и единственный, кто сверяет план с исходом; на его вход подаётся этот план вместо плана разметки. Тема requirements в плане отсутствует за отсутствием предмета; security и architecture закрыты только сверкой с записанными инвариантами внутри code — третья половина этого прохода включается здесь по той же причине, что и при метке small. Все три обязаны быть названы в границах покрытия, а сигнал о заниженной метке на таком прогоне не работает: поднимать нечего.

Дом плана — сценарий, а не этот скилл: av-dev:code-resolve, references/maintain.md, раздел «Ревью — план фиксирован сценарием».

Правило гейта на таком прогоне работает жёстче обычного. Правка, которая трогает сам гейт, проверяется гейтом же — инструмент проверяет себя, — поэтому сверяется не только цвет, но и состав шагов. Что считается составом, объявляет проект семантикой гейта в CLAUDE.md; не объявил — это строка границ покрытия, а не догадка прохода.

Ступень 1 — Автотесты (обязательна при любой метке)

Агент review-autotests, тема autotests. Гонит команду гейта из семантики гейта в CLAUDE.md — либо засчитывает прогон, сделанный до ревью, — и интерпретирует вывод.

Тема и проход названы одинаково намеренно, а «гейт» осталось именем команды. Раньше тема звалась autotests, а проход — gate: одна сущность под двумя именами, и вопрос проекта, адресованный одному имени, к другому не приезжал. Слово «гейт» теперь значит ровно одно — барьер, который проект запускает; тема шире него ровно на «чего в гейте намеренно нет».

Гейт, прогнанный до ревью, второй раз не гоняется. Задача приходит на ревью с зелёным гейтом: сценарий решения доводит его до зелёного шагом opsx:apply, сценарий обслуживания — своим шагом гейта. Повтор на неизменившемся дереве вернёт тот же вывод, а стоит он минут — то есть платит ими ни за что.

Признак один и проверяемый — отпечаток рабочего дерева. Его снимают дважды: тот, кто прогнал гейт, сразу после прогона, и проход перед началом работы.

{ 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 или 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-proofдве темы разом, security и operations. Набросок пути (вход, преобразование, куда легло) и ось времени (миграция и откат, рост, удержание, чужая деградация). Строит сценарий рассуждением и ничего не запускает; потолки раздельные — 2 находки на тему;
  • review-architecture, тема architecture — концептуальная целостность на входе шире диффа.

Доказательства на этой ступени больше нет, и это решение по цене. Прежде обе темы закрывала пара тяжёлых проходов: review-adversary строил путь и прогонял падающий тест, review-ops снимал числа замером. Оба держали машину, шли цепочкой и стоили часов на каждой задаче, где запускались.

Пара никуда не делась — её зовёт скилл av-dev:code-deep-review, который идёт не на задаче, а по названной области и время от времени. Замер, ради которого её и держали, остался: враждебный проход дал пять из семи выживших находок дозапуска на пяти задачах подряд, эксплуатационный — единственный, кто нашёл, что откат бинаря поверх новой схемы стартует молча. Ценность этой пары оплачивалась на каждой задаче, а получалась на немногих; теперь она оплачивается тогда, когда её решают получить.

review-proof копит вход глубокому прогону. Всё, что доказывается только запуском, он не выдаёт находкой и не выбрасывает: строка в границах покрытия называет тему, место и запуск, которым это проверяется. Строка — единственный вход av-dev:code-deep-review, заводящийся по ходу обычной работы.

Чем платит цикл, названо прямо. critical эта ступень больше не присваивает: его оракул добывается запуском. Дефект, который виден только под нагрузкой — гонка, деградация, исчерпание ресурса, — в цикле задачи не ловится ничем; это идёт строкой в границы покрытия каждого прогона и разобрано в «Честном пределе».

Дома тем приходят из плана разметки задачи: security и operationsreview-proof, architecture (устройство, граница домена) — архитектурному. Что с чем сшивать и почему — project-facts.md, раздел «Сшивать обязаны проходы». Без домов ступень вырождается в общие места.

Числа и решения проекта эта ступень не читает. research.* и adr.* — процессные документы, и прогон их не открывает. Для review-proof это значит, что чужое число ему не оракул: он его не снимал, а свои он не снимает вовсе. Для архитектурного — что граница домена берётся из passport.*, а не из истории решений. Обе потери названы в «Честном пределе».

Условие ступени и есть условие метки large: изменение крупное или незнакомое — любая из двух осей. Разведены они не для красоты: у архитектурного прохода работа появляется от размера (трогается несколько слоёв разом или в проекте становится больше сущностей, чем было), у меряющей пары — от сложности (форму решения нащупывали по ходу, и потому неизвестно, где она протекает). На малом и знакомом вопрос «не появился ли второй способ» отвечается «нет» до запуска, а построенный путь строить негде.

review-architecture получает вход шире диффа: дерево пакетов с назначением, граф внутренних зависимостей, инвентарь существующих концепций. Команду, которая это готовит, даёт раздел команд CLAUDE.md; нет команды — проход собирает карту сам и говорит об этом в границах покрытия.

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

Ступень 5 — Triage (обязательна)

Агент review-triage. Единственный сток графа и единственный, кто агрегирует. Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не стартует. Получает сырые выводы всех проходов, git diff, режим и план разметки задачи; возвращает финальный отчёт.

На прогоне без change его место занимает план сценария — см. «Прогон без change»: сверять исход с планом триаж обязан и там, а другого плана в том прогоне не существует.

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

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

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

Порядок: дедупликация по причине → оракул для всего critical/major → понижение неподтверждённого до гипотезы → отсев вкусовщины → ранжирование по ущербу × вероятности → потолок 7 пунктов в основном списке.

Контракт находок

Единый для всех проходов — references/finding-contract.md. Коротко: заголовок через последствие, обязательные поля Файл, Severity, Confidence, Оракул, Последствие, Предложение, Найдено проходом. critical без оракула или построенного пути не существует. Находка без поля «Последствие» не выводится вовсе.

Каждый проход завершает вывод блоком ## Coverage of this pass.

Что происходит с находками дальше

  • Оркестратор чинит помеченное Действие: инлайн и не логирует мелочь.
  • Действие: развилка — вопросом с вариантами и ценой каждого туда, где проект держит вопросы (это знает вызвавший скилл, а не конвейер ревью). Оркестратор не останавливается: он урезает изменение до остатка и доводит его.
  • Находка не для этого мерджа, но реальная (отложенный major, развилка, решённая «потом»), — не теряется, но и не заводится здесь. Конвейер отдаёт её списком урожая в отчёте: формулировка, оракул, откуда взялась (какой проход, какой change). Заведение задач принадлежит av-dev:task-track — зови его со списком урожая, у него на этот вход отдельный сценарий «задачи из ревью и аудита»: свой формат, кластеризация по причине, дедуп против беклога и кладбища. Каталога задач в проекте нет — урожай остаётся списком в отчёте, и это говорится строкой доклада: задачи из него не заведёт никто. Мелочь класса nit идёт в урожай одной пачкой, а не записью на находку.
  • Promote candidates — по процедуре references/promote.md: находка → конвенция → правило линтера → удаление формулировки из конвенций. Третий шаг обязателен.
  • Дефект, проскочивший ревью и всплывший позже, идёт в журнал проекта (references/review-journal.md) — сразу, не ретроспективно: теряется именно то, почему дефект не поймали.
  • Отчёт триажа сохраняется вместе с изменениемopenspec/changes/<id>/review/. Он единственное, по чему потом видно, что было найдено и что из этого не заведено: нулевой урожай при непустом отчёте виден сразу. Вместе с изменением он и переезжает: после opsx:archive его адрес — openspec/changes/archive/<id>/review/. Кто ищет отчёт после архивации (приёмщик на груминге av-dev:task-groom, разбор дефекта), смотрит оба пути; «отчёта нет» объявляется, только когда пуст и архивный, иначе самый дорогой сценарий «состав ревью неизвестен, гоняем заново» срабатывает на каждой доведённой задаче.

Честный предел

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

Согласие нескольких проходов — не подтверждение: это один источник, высказавшийся несколько раз. Совпадение повышает приоритет, но не confidence.

Что недоступно этому проекту принципиально — перечисляет «Недоступно проверке» в docs/review.*, по темам, и оба его подраздела целиком уезжают в границы покрытия. Тема, у которой нет дома, — тоже граница покрытия, и она объявляется планом на каждом прогоне, а не разово. Независимо от проекта недоступно:

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

Отдельно и честно: поимённая сверка с положениями руководств по стилю языка не задаётся ни одним проходом. Проход про идиоматичность упразднён, его способные части переселены (эксперимент против поведения библиотеки и драйвера — в ops, вопрос 8, а он теперь в av-dev:code-deep-review; «не изобретаем ли то, что уже есть в библиотеке» — в architecture, вопрос 1), но различение «идиоматично против распространено» теперь не спрашивает никто. Класс обратимый — портит форму кода, не данные, — и его надо признавать в границах покрытия, а не считать проверенным.

На small и medium ничего не проверяется запуском сверх гейта. Это самая крупная граница покрытия конвейера, и она обязана идти строкой в каждом таком прогоне — поимённо, а не общим «метка ниже». Формулировка «не запускается ничего» была бы короче и была бы ложью: гейт запускает инструменты проекта, а триаж проверяет оракул critical/major запуском — оба идут при любой метке. Не проверяется проходом с мнением: построенный путь атаки (его надо прогнать), поведение библиотеки и драйвера в вырожденном случае (достаётся только экспериментом), любое число — время удержания блокировки, пик кучи, темп роста журнала, стоимость на годовой истории. basics задаёт часть тех же вопросов чтением, и его ответы поэтому слабее: он формулирует условиями, оракула не приносит и выше гипотезы находку не поднимает — кроме той, что опирается на инвариант CLAUDE.md.

На small три темы ядра смотрятся только против записанных инвариантов. Отдельная строка, и она обязательна на каждом прогоне small: security, operations и architecture закрывает не приёмник тем, а code сверкой с CLAUDE.md, потолком 1 находка на все три. Свойства, которого нет в инвариантах, с этой меткой не спросит никто. Это не «глубина ниже» — это другой дом темы, куда более узкий, и путать одно с другим нельзя.

Решения и измеренные числа проекта прогон не читает вовсе. adr.* и research.* — процессные документы. Отсюда две строки в границы покрытия каждого прогона: расхождение изменения с записанным решением ловится не здесь, а сверкой документации; число, на которое опирается находка, обязано быть снято на этом прогоне, иначе находка не поднимается выше гипотезы. Раньше числа брались из docs/research/, и находка выглядела доказанной чужим замером неизвестной свежести.

Темы при этом названы все — но закрыты они по-разному, и это надо читать буквально. «Тема security, глубина сверка» не значит «безопасность проверена»: значит, что дом темы открыли, дифф посмотрели и сравнили.

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

Это сознательная сделка, а не пробел в устройстве: пара меряющих проходов оплачивалась на каждой задаче с меткой large, а получалась на немногих. Теперь она живёт в av-dev:code-deep-review и оплачивается тогда, когда её решают получить. Проверяется сделка не рассуждением, а двумя следами: строками «отложено» в отчётах — если по одному месту повторяется один и тот же неснятый замер, глубокий прогон просрочен, — и журналом дефектов: класс, всплывающий после мерджа, значит, что прогон надо звать чаще.

Так же честно и про упразднённый проход: «не знаю, чего не знаю» больше не достаёт никто. Проход независимой реализации писал свою версию узла, не открывая существующую, и диффил по решениям — декомпозиция, владение данными, модель конкурентности, форма решения там, где спека выбора не сделала. Он снят по решению оператора о стоимости — счёт определялся объёмом вывода, и на прогон он тратил больше всех остальных проходов вместе, — а не по замеру, который calibration.md требует перед удалением. Значит и записывается это как сознательное сужение, а не как «класс оказался пустым»: остаток независимого взгляда даёт architecture (второй способ, лишние слои), но альтернативной реализации, с которой можно сдиффить решения, у конвейера теперь нет. Сузилось это дважды: вместе с проходом независимой реализации ушла и стадия ревью дизайна, ловившая форму решения до того, как код написан. Класс идёт строкой в границы покрытия каждого прогона — там же, где проект перечисляет своё в подразделе «перестали проверять сознательно».

Это и есть причина, по которой конвейер готовит ревью, а не заменяет его.

Ссылки

  • references/project-facts.md — что нужно проходу и где это лежит в документах проекта; таблица поразрядной деградации.
  • references/review-levels.md — дом правила выбора метки: две оси, спорное вниз, чем small дешевле, доли как проверка правила.
  • Skill av-dev:code-deep-review — глубокое ревью области кода: там живут review-adversary и review-ops, там же единственное место процесса, где находка доказывается прогоном и замером.
  • Skill av-dev:canon — приведение проекта к канону документов.
  • references/finding-contract.md — контракт находок.
  • references/promote.md — промоут находка → конвенция → правило → удаление.
  • references/calibration.md — калибровка инъекцией, вердикты keep/retune/drop.
  • references/review-journal.md — журнал проскочивших дефектов.