Files
dev-skills/av-dev-pipeline/skills/review-pipeline/SKILL.md
T
avandClaude Opus 5 d5bee11a6b классификация задачи: три категории документов и метка вместо ступени
Канон 5 объявил «каждый документ docs/ — тема ревью». Правило верно ровно
наполовину и потому вредно целиком. Паспорт и схему хранилища ревью читает, но
темами они не являются: по ним нельзя сказать «в этом изменении сделано не так»,
они задают границу, по которой судит чужая тема. Журнал решений и журнал
наблюдений ревью изменения не нужны вовсе — ADR объясняет прошлое, а не
предъявляет требование. Разметчик, применявший правило буквально, обязан был
либо завести фантомные темы passport, adr, database, research и продублировать
ими работу architecture и operations, либо потерять четыре документа молча;
случались обе ветки, и в собственном образце плана docs/passport.md не попадал
ни строкой, а обязательная арифметика покрытия при этом не сходилась.

Категорий теперь три, разрез проверяемый. Тема — да, прямо: conventions,
security, architecture и любой свой документ проекта. Источник темы — нет, но он
задаёт границу для чужой: passport, database, CLAUDE.md, openspec/specs.
Процессный — нет, он про то, как мы работаем: tasks, review, adr, research,
.pm.json. Открыта одна категория из трёх, две другие перечислены поимённо, так
что документ вне раскладки — однозначно своя тема. adr и research прогон больше
не открывает ни одним проходом; docs/review остаётся читаемым, но как настройка
конвейера, а не критерий. Цена записана и стала обязательной строкой границ
покрытия: расхождение с записанным решением ловит теперь только сверка
документации, а число под находкой обязано быть снято на этом прогоне, с
приложенной командой.

Классификация выдаёт задаче метку — small, medium, large. Прежние quick,
standard и wide назывались ступенью и описывали ревью: как глубоко смотрим.
Классифицируется же задача, и пока величина называлась свойством прогона, её
естественно было пересчитывать на каждом прогоне — что конвейер и делал. Слово
«ступень» удалено, а не оставлено синонимом: два имени одной вещи расходятся.
Выводится метка из двух разведённых осей — размер (малое, среднее, крупное) и
сложность (знакомое, незнакомое), — и равна максимуму по ним. Метка не синоним
размера: малое незнакомое изменение получает large, трогая один узел, поэтому
план печатает три строки с обоснованием каждая и выводить одну из другой
запрещено. Оси остались русскими словами — это суждение прозой; метка
английская — это идентификатор, который проходы сравнивают.

Разметка переехала из ревью кода в шаг 4 пайплайна, сразу после propose. Она
шла первым проходом каждого ревью кода, а перед ревью дизайна ту же величину
называл сам пайплайн — то есть оркестратор, который только что довёл
предложение до propose. Одно и то же измерялось дважды, и один из двух раз без
разведённости с автором, ровно в той точке, ради которой разметчик заведён.
Теперь запуск один на задачу, диффа он не видит, план обслуживает обе стадии, и
метка после кода не пересматривается: расхождение факта с разметкой ловит журнал
дефектов постфактум, как и всякую другую ошибку выбора. На диск план не пишется —
четвёртый артефакт рядом с proposal, tasks и design пережил бы задачу и разошёлся
бы с ней молча.

Ревью дизайна тоже растёт меткой: small — specs, medium — плюс rubric, large —
плюс architecture и вопрос автору о трёх формах решения. Раньше rubric и
architecture включались одним условием, и medium получал ровно один проход, то
есть не отличался от quick ничем. Разведены они потому, что зарабатывают на
разном: рубрика порождает свойства узла и окупается уже на среднем изменении,
её выход уезжает приёмочными критериями в tasks.md; архитектура отвечает на
вопрос про второй способ, а он на среднем знакомом изменении отвечается «нет»
ещё до запуска.

small подешевел тремя способами сразу. Составом: приёмник тем не запускается,
три темы ядра переходят к code сверкой по записанным инвариантам CLAUDE.md с
потолком в одну находку, и это не «глубина ниже», а другой дом темы. Входом:
specs читает только дельта-спеку, code — только индекс конвенций. Потолком: он
появился у каждого опиниативного прохода, а не у одного basics, и у половин code
он раздельный, потому что конвенционных находок больше по построению и в общем
списке они вытеснили бы техническую половину. Сработавший потолок обязан быть
объявлен строкой — молчащий срез неотличим от «больше не нашлось». Отрицательный
тест small от этого стал жёстче, а не мягче: вопросы про обратимость миграции
задавал приёмник тем, и на этой метке их не задаст никто.

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

Проверено прогоном ревьюверов по готовому результату: девять расхождений найдено
и починено — контракт находок печатал старый перечень проходов вместо плана по
темам, три ссылки в task-batch указывали на шаг коммита вместо закрытия, запись
changelog не переводила вопросы, адресованные passport и database, ops и
adversary утверждали, что на нижних метках их вопросы задаёт basics, шаблон
покрытия в review-code зашивал потолки small намертво, триггеры метки рассыпались
на два списка против трёх, тема из директивы CLAUDE.md могла остаться без запуска
исполнителя. Гейт зелёный: фронтматтеры, копии, одиннадцать диаграмм, ruff,
pyrefly; docs.py прогнан на живом фикстуре и печатает категорию в отказе.

Канон повышен до версии 6 с записью, выполнимой upgrade. Решения — 40–44.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:57:15 +03:00

104 KiB
Raw Blame History

name, description
name description
review-pipeline Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — 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 (финальная сверка).

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

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

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

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

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

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

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

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

Предпосылки

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

  • OpenSpec — жёсткая предпосылка, а не опция. Ревью дизайна, проход review-specs и вызывающий пайплайн задачи завязаны на дельта-спеки (openspec/changes/<id>/specs/*/spec.md), на актуальные спеки (openspec/specs/) и на openspec validate --strict. В проекте без OpenSpec шаги, зовущие opsx:explore / opsx:propose / opsx:apply / opsx:archive, упадут на «нет такого скилла», а review-specs останется без источника требований. Проект без OpenSpec этим конвейером не проверяется — подключай OpenSpec, а не понижай прогон: ветка деградации здесь не пишется, потому что непроверенная ветка деградации хуже честного отказа.
  • Документы канона — см. следующий раздел.
  • Проектные копии этих скиллов и агентов удаляются при установке. Если в проекте уже лежат свои .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. Отдельного файла-брифа при этом нет: пути известны, посредник не нужен, а второй дом для тех же фактов разошёлся бы и выглядел актуальным.

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

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

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

  • его темы — какие темы он закрывает на этом прогоне, у каждой дом (путь и раздел) и глубина. Дом передаётся адресом, а не пересказом: проход, получивший проинтерпретированный периметр, не заметит, что интерпретация неверна;
  • вопросы по его темам из docs/review.*, если они там есть, — дословно. Вопрос привязан к теме, а не к имени прохода, и потому переживает переезд прохода между метками;
  • контракт находок — путь к 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, правило «спорный случай вниз» и сигнал basics о заниженной метке. Дешёвая модель безопасна ровно потому, что её вывод устроен как список, а не как мнение.

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

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

  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, разбор

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

flowchart TD
    propose["opsx:propose — change, дельта-спеки, tasks.md"]
    scope["review-scope — разметка задачи<br/>размер × сложность → МЕТКА"]
    label{{"метка"}}

    subgraph design["Ревью дизайна — до кода"]
        dS["specs — всегда"]
        dR["+ rubric"]
        dA["+ architecture<br/>+ вопрос автору о трёх формах"]
    end

    apply["opsx:apply — код, гейт зелёный"]

    subgraph code["Ревью кода — после apply"]
        cGate["autotests — гейт, источник графа"]
        cS["specs — requirements"]
        cC["code — conventions + техника"]
        cInv["code, третья половина:<br/>security, operations, architecture<br/>против инвариантов CLAUDE.md"]
        cB["basics — темы ядра + свои темы"]
        cBown["basics — только свои темы проекта"]
        cHeavy["adversary · ops · architecture<br/>доказательство"]
        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 при своих темах) 56 много
medium рабочее умолчание: среднее и знакомое specs, rubric 1, 2, 3, 5 7 большинство
large крупное или незнакомое: большой рефакторинг, функциональность, форму которой ещё предстоит нащупать specs, rubric, architecture 1, 2, 4, 5 (+3 при своих темах) 1011 510%

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

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

small дешевле medium тремя разными способами сразу, и каждый назван.

  1. Составом. basics на small не запускается — кроме случая, когда у проекта есть свои темы; тогда он идёт только с ними, ровно как в large. Три темы ядра, которые он держал бы, переходят к code сверкой по инвариантам.
  2. Входом. На small specs читает только дельта-спеку, а code — только индекс конвенций (перечень родов и что механизировано), не весь их дом. На medium оба читают дома целиком.
  3. Потолком. На small потолки жёсткие и напечатаны: specs — 3 находки, code — 3 технических плюс 2 конвенционных плюс 1 по трём темам ядра.

Что small за это не проверяет, названо поимённо и обязано идти строкой в границы покрытия: темы security, operations и architecture смотрятся только против записанных инвариантов CLAUDE.md. Свойство, которого в инвариантах нет, с этой меткой не спросит никто — ни сценарием, ни чтением дома темы. Это и есть цена метки, и она заметно больше прежней: раньше small отличался от medium одним проходом на один вопрос, то есть не экономил ничего и назывался отдельной меткой зря.

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

large назван по тому, что он добавляет: вход шире диффа. Он единственный, где живут тяжёлые проходы, и единственный, где что-то запускается. basics в нём берёт только проектные темы; своих тем у проекта нет — он не запускается вовсе, и план говорит об этом строкой. На small действует то же правило и по той же причине — приёмник запускается только тогда, когда ему есть что принимать. Совпадение неслучайное: basics держит темы ядра ровно при одной метке из трёх, а приёмником проектных тем работает на всех.

Доля 5–10% — не пожелание, а проверка правила. Если large уходит каждая третья задача, метку выбирают по ощущению важности. Обратный перекос виден по журналу проскочивших дефектов: класс, который ловят только меряющие проходы, начинает всплывать после мерджа.

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

Правило выбора — две оси, а не один вопрос

Дом правила здесь, а применяет его review-scope при разметке задачи — не автор изменения. Оси две, они измеряют разное, и метка есть максимум по ним.

знакомое — форму решения можно назвать до начала незнакомое — форму предстоит нащупать по ходу
малое — один узел small large
среднее — несколько узлов одного слоя medium large
крупное — несколько слоёв, перенос ответственности, большой рефакторинг large large

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

Размер — про объём: сколько мест трогается. Сложность — про неизвестность: знаем ли мы форму решения заранее. Признак незнакомого простой и проверяемый: перед работой нельзя назвать, какие узлы будут тронуты.

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

Оси называются и на стадии дизайна, и на стадии кода — но считаются один раз. Это и есть причина, по которой разметка переехала к propose: состав ревью дизайна выводится из той же пары, что и состав ревью кода, а считать её дважды значит один раз посчитать без разведённости с автором.

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

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

Что здесь считается крупным, что — незнакомым и что — мелким, проект уточняет в docs/review.md, подразделе «Триггеры метки»: тремя списками — по одному на каждую ось вверх и один вниз, поимённо, узлами или capability. Это уточнение, а не отмена: не записано — работает таблица выше.

Спорный случай решается вниз, и у этого есть цена

Правило асимметрично, потому что асимметрична цена ошибки.

  • Спорно между medium и large → бери medium. Ошибка в эту сторону стоит находки, которая всплывёт на следующей задаче или в журнале дефектов. Ошибка в обратную стоит трёх тяжёлых проходов, двое из которых держат машину и идут цепочкой, — и платится она на каждой задаче, выбранной неверно.
  • Спорно между small и medium → бери medium. Раньше эта строка обосновывалась тем, что состав одинаков и ошибка почти бесплатна. Теперь состав разный, и обоснование стало прямо противоположным: на small три темы ядра смотрятся только против записанных инвариантов, а спорный случай — ровно тот, где неизвестно, покрыт ли он инвариантом. Сомнение здесь стоит дороже, чем раньше, и потому решается вниз тем более твёрдо.

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

  • границы покрытия называют темы и их глубину, а не только запущенные проходы — иначе small выглядит так же, как large без находок;
  • журнал дефектов в docs/review.md перестаёт быть хорошей практикой и становится единственной обратной связью: проскочивший дефект — единственный сигнал, что метка выбрана слишком низко;
  • возврат в код — повод пересмотреть метку. Задача, которая приходит в тот же узел третий раз, уже не мелкая, чем бы ни выглядел её дифф.

Метка — максимум по поверхности

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

Обратное тоже верно и тоже не бесплатно: у каждой задачи есть несокращаемый костяк — гейт, спеки, код, триаж. Разрезать задачу, обе половины которой остаются в одной метке, значит заплатить костяк дважды за ту же проверку. Резать стоит там, где разрез снимает доказательство с большей части диффа. Шов и правило нарезки живут у того, кто ведёт задачи, — скилл av-dev-pm:tasks, его references/split.md. Пути туда конвейер не выносит: за пределы своего плагина он ходит вызовом скилла, а не файлом.

Разметка в костяк не входит — она платится один раз на задачу, а не один раз на прогон, и потому разрез задачи её не удваивает. Это единственное, что стало дешевле от переезда разметки к propose, и это же снимает прежний довод против нарезки.

Размер, сложность, метка и глубина объявляются в отчёте, и все четыре с обоснованием. Метка выбирает review-scope; он вправе и поднять, и понизить её — но не молча: строка «метка X, потому что размер Y и сложность Z» обязательна на каждом прогоне, а не только когда метка отличается от ожидаемой.

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

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

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

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

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

flowchart TD
    plan[/"план разметки задачи<br/>(готов до ревью кода)"/]
    autotests["autotests<br/>(стадия 1, держит машину)"]
    specs["specs"]
    code["code"]
    basics["basics<br/>(medium: темы ядра и свои;<br/>small, large: только свои темы проекта)"]
    adversary["adversary<br/>(large, держит машину)"]
    ops["ops<br/>(large, держит машину)"]
    architecture["architecture<br/>(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, и сразу триаж. На smallspecs и 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 ревью кода и платилась на каждом прогоне, а перед ревью дизайна тот же вопрос — «крупное или незнакомое?» — задавал сам вызывающий, то есть оркестратор, который только что написал предложение. Одна и та же величина считалась дважды, и один из двух раз — без разведённости с автором. Теперь она считается один раз и обслуживает обе стадии.

Что он читает. Запись задачи, proposal.md и дельта-спеки change, tasks.md change, docs/ на уровне имён и заголовков, CLAUDE.md и AGENTS.md, docs/review.* — раздел настройки. Диффа он не читает: кода ещё нет. Размер он оценивает по перечню границ задачи и по дельта-спекам, а не по git diff --stat.

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

Он не судит по существу — ни одной находки об изменении. Его ошибка это пропущенная тема или не та метка, и обе видны: разнесение документов сверяется с ls docs/ за секунду, а заниженную метка ловит basics своим сигналом.

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

План живёт в контексте прогона задачи и на диск не пишется. Файл-план был бы четвёртым артефактом рядом с 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, раздел «Сшивать обязаны проходы». Без домов стадия вырождается в общие места.

Числа и решения проекта эта стадия больше не читает. 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 в режиме «дизайн ДО кода». Дельта-спеки сверяются на каждой задаче: это самый дешёвый чекпоинт конвейера, и он ловит то, что на готовом коде уже не чинят;
  • со mediumreview-rubric (фаза 1 без фазы 2: рубрика на задуманный узел становится приёмочными критериями и уезжает в tasks.md);
  • только в largereview-architecture на предложении: можно ли выразить существующими понятиями — включая конструкции стандартной библиотеки, — не появляется ли второй способ. Вопрос «не изобретаем ли то, что уже есть в библиотеке» живёт здесь; тогда же задаётся вопрос автору дизайна: «предложи три формы решения и назови компромисс каждой» — если ответ показывает, что рассматривалась одна, это находка.

Рубрика съехала на метку вниз, а архитектура осталась наверху — и это не симметричная правка. Раньше оба прохода включались одним условием, и medium получал на предложении ровно один проход, то есть не отличался от small вовсе. Разводятся они потому, что зарабатывают на разном: рубрика порождает свойства узла и окупается уже на среднем изменении — её выход уезжает приёмочными критериями в tasks.md и работает потом на всей задаче; архитектура отвечает на вопрос «не появился ли второй способ», а он на среднем знакомом изменении отвечается «нет» ещё до запуска. Держать её ниже large значит платить за предсказуемый ответ на каждой задаче.

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

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

flowchart TD
    plan[/"план разметки задачи:<br/>размер, сложность, метка"/]
    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. Коротко: заголовок через последствие, обязательные поля Файл, Severity, Confidence, Оракул, Последствие, Предложение, Найдено проходом. critical без оракула или построенного пути не существует. Находка без поля «Последствие» не выводится вовсе.

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

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

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

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

Ссылки

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