Тема звалась autotests, а закрывающий её проход — gate, и на всех трёх ступенях это была одна и та же клетка таблицы. Одна сущность под двумя именами — та же ошибка, что и два разных под одним, только тише: она не путает, а теряет. Вопрос проекта в docs/review адресуется теме; адресованный проходу не приезжает никуда, и ровно этот отказ уже случился однажды с ops. Победило имя темы. Тема первична по правилу 0, а имена тем — это имена документов: docs/autotests.md проект напишет (что покрыто, что нарочно нет, где testdata), docs/gate.md не напишет никто, потому что гейт это команда, а не предмет. Слово «гейт» к тому же занято дважды — команда проекта и ребро графа; третьим значением стал бы нечитаемым отчёт, где «гейт красный» и «гейт нашёл» про разное. И тема шире гейта ровно на «чего в гейте намеренно нет». Цена названа честно: autotests звучит уже своего содержимого — линт, типы и сканер уязвимостей тестами не являются. Гасится строкой в уставе: тема — это «проверено ли машиной», а не «есть ли тесты», гейт в ней инструмент, а не граница. Слово «гейт» осталось ровно в одном значении — команда проекта. Все прочие вхождения (семантика гейта, «пока гейт красный», финальный гейт в task-batch) именно про неё и не тронуты. Побочно: autotests — единственная тема, чей дом лежит не в docs/, а в CLAUDE.md. Канон править не пришлось: список тем открытый, и заведённый когда-нибудь docs/autotests.md ляжет на существующее имя. Тема 37 в DECISIONS.md, следствия 141-142. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
78 KiB
name, description
| name | description |
|---|---|
| review-pipeline | Конвейер ревью изменения, устроенный по темам: каждый документ проекта — тема ревью, а проход лишь закрывает тему на заданной глубине. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список открытый, свои темы проект заводит документом. Прогон начинает разметчик: находит документы, выводит темы, выбирает ступень с обоснованием и раздаёт темы проходам. Три ступени: quick и standard закрывают все темы (сверкой и разбором), wide добавляет доказательство — враждебные постановки, эксплуатационный постмортем, архитектурный проход на широком входе. Триаж обязателен всегда. Порядок прогона — граф зависимостей: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода. |
Конвейер ревью
Готовит ревью — не заменяет его. Потребитель отчёта — оркестратор, который чинит код; человек читает только сводку, развилки и границы покрытия.
Четыре правила, из которых всё следует
Если ситуация не покрыта инструкцией — решай по ним.
-
Тема первична, проход вторичен. Ревью проверяет темы — набор направлений, который проект объявляет своими документами. Проход это только способ закрыть тему на заданной глубине, и проходы меняются: уезжают в верхнюю ступень, сливаются, упраздняются. Если состав прогона считать списком проходов, то уехавший проход уносит тему с собой беззвучно — отчёт честно скажет «
opsне запускался» и не скажет «эксплуатацию не смотрел никто», а нужно второе. Поэтому прогон описывается таблицей «тема → глубина → кто закрывает», и таблица эта есть в каждом отчёте. -
Recall чек-листа равен длине чек-листа. Проход, устроенный как «проверь пункты 1..N», найдёт ровно перечисленное. Всё неявное — идиомы, форма решения, «так не делают» — неперечислимо по определению: перечислимое уже стало бы конвенцией. Отсюда деление проходов на applicative (применяют заданный критерий) и generative (сперва порождают критерий или альтернативу, потом сравнивают). Расширять чек-листы бесполезно; неявный слой достают только generative-проходы.
-
Ценность верификатора = наличие внешнего оракула × разведённость с автором, а не число ролей. Под всеми ролями одна модель с одними априорными, вход у всех общий: седьмая роль почти не добавляет recall, но линейно удорожает триаж. Иерархия надёжности: детерминированный инструмент > агент, который его запускает и интерпретирует вывод > агент с чистым мнением. Максимум работы переносим вниз.
-
Отчёт без границ покрытия хуже отсутствия отчёта. «Критичных проблем не обнаружено» потребляет ощущение проверенности, ничего не гарантируя. Секция границ покрытия обязательна и не сокращается — в том числе в докладе человеку.
Предпосылки
Конвейер опирается на внешнюю обвязку и без неё работает не целиком. Проверь это один раз, при установке плагина в проект:
- OpenSpec — жёсткая предпосылка, а не опция. Профиль
design, проход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.
Темы — и почему их список открытый
Каждый документ проекта — тема ревью. Форма дома значения не имеет:
docs/security.md и docs/security/ — одна тема security, проект выбирает
форму по объёму написанного. Завёл документ — завёл тему; запретить нельзя,
разрешения не надо.
Из этого следует то, ради чего правило и заведено: docs/ перестаёт быть просто
документацией и становится конфигурацией конвейера. Проект настраивает ревью
тем, что пишет о себе, а не отдельным файлом настроек, который разошёлся бы с
документами.
Ядро — шесть тем, они есть у любого проекта, приведённого к канону:
| Тема | Дом | Вопрос темы |
|---|---|---|
requirements |
openspec/specs/, дельты change |
делает ли код заказанное, и только его |
autotests |
CLAUDE.md: семантика гейта, команды |
проверено ли машиной и хватает ли проверок |
conventions |
docs/conventions.* |
написано ли так, как здесь пишут |
architecture |
docs/architecture.*, passport.*, adr/ |
цело ли устройство: понятия, границы, решения |
security |
docs/security.* |
что сделает недоверенный вход |
operations |
docs/architecture.*, database.*, research/ |
что будет через неделю на проде |
Не темы — их ровно две: docs/tasks/ (каталог задач, его ведёт
av-dev-pm:tasks) и docs/review.* (настройка самого конвейера и журнал
дефектов — слой над темами). Обе называются в плане явно, а не пропускаются
молча.
Проектная тема закрывается basics, на любой ступени. Именных проходов
конечное число, а тем — сколько заведёт проект; приёмник обязателен, иначе
открытость списка была бы обещанием без механизма.
Тема без дома — законное состояние и отдельная строка. «Тема operations
заявлена, docs/database.md нет» читается иначе, чем «не смотрели». Деградация
поразрядная: нет дома — падает глубина этой темы, и только её.
Что именно проход читает по каждой теме — references/project-facts.md. Отдельного файла-брифа при этом нет: пути известны, посредник не нужен, а второй дом для тех же фактов разошёлся бы и выглядел актуальным.
Документов канона нет вовсе — проект не приведён к канону. Скажи это строкой
и предложи скилл av-dev-pm:canon: одна операция на проект против деградации на
каждой задаче. Прогон при этом не останавливается.
Что получает каждый проход
Задание собирается по плану разметчика (стадия 0) и состоит из шести вещей:
- его темы — какие темы он закрывает на этом прогоне, у каждой дом (путь и раздел) и глубина. Дом передаётся адресом, а не пересказом: проход, получивший проинтерпретированный периметр, не заметит, что интерпретация неверна;
- вопросы по его темам из
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 внизу, и это не противоречие, хотя его ошибка расходится дальше всех.
Его работа распадается надвое: поиск документов и раздача тем перечислимы —
план сверяется с ls docs/ за секунду, пропущенный документ виден без
рассуждения; выбор ступени — суждение, но у него есть три независимых
корректора: отрицательный тест quick, правило «спорный случай вниз» и сигнал
basics о заниженной ступени. Дешёвая модель безопасна ровно потому, что её
вывод устроен как список, а не как мнение.
Самая дешёвая модель не используется ни на одном проходе, и это не экономия наоборот. Дешёвая модель на опиниативном проходе даёт правдоподобные находки, которые триаж обязан опровергать оракулом, — а это самая дорогая операция конвейера. Механизируемая же работа здесь вынесена ниже модели: гейт, покрытие диффа, карта проекта — это скрипты проекта, они стоят ноль токенов. Дешёвому проходу просто не осталось работы.
Экономия достигается не понижением модели, а глубиной и непуском: quick и
standard закрывают все темы, но чтением и рассуждением, а wide добавляет
доказательство — запуск, замер, построенный путь. Именно доказательство и стоит
часов: машина, цепочка меряющих проходов, ожидание.
Второй рычаг — вход и потолок прохода. basics идёт на верхней модели, но с
узким входом (дифф и его окрестности, без карты проекта) и жёстким потолком: 2
находки на сверке, 4 на разборе. Дешевле он не от модели, а от того, чего не
делает.
Профили
Ступень не меняет список тем — она меняет их глубину. Все темы закрыты во всех профилях; разница в том, читают ли их, рассуждают над ними или доказывают запуском.
| Тема | quick |
standard |
wide |
|---|---|---|---|
requirements |
specs |
specs |
specs |
autotests |
autotests |
autotests |
autotests |
conventions |
code |
code |
code |
architecture |
basics, сверка |
basics, разбор |
architecture, доказательство |
security |
basics, сверка |
basics, разбор |
adversary, доказательство |
operations |
basics, сверка |
basics, разбор |
ops, доказательство |
| тема проекта | basics, сверка |
basics, разбор |
basics, разбор |
Отсюда состав:
| Профиль | Когда | Стадии | Проходов | Доля задач |
|---|---|---|---|---|
quick |
мелкое: багфикс, мелкая фича, локальная правка, доки | 0, 1, 2, 3, 5 | 6 | много |
standard |
рабочее умолчание: всё, что не мелкое и не крупное | 0, 1, 2, 3, 5 | 6 | большинство |
wide |
крупное или незнакомое: большой рефакторинг, функциональность, форму которой ещё предстоит нащупать | 0, 1, 2, 4, 5 | 8 | 5–10% |
design |
до кода, на предложении | specs, плюс rubric и architecture по условию wide |
1–3 | — |
quick и standard совпадают составом и различаются глубиной — это
единственное место конвейера, где профиль не выводится из одного лишь списка
проходов. Поэтому глубина объявляется в отчёте наравне с профилем, а план
разметчика называет её по каждой теме. Проверять надо два факта вместо одного, и
оба напечатаны.
Три глубины, и они не про старательность, а про способ доказательства. Сверка — открыть дом темы, открыть дифф, сравнить. Разбор — построить сценарий рассуждением, ничего не запуская. Доказательство — прогнать, померить, построить путь. Только третья требует машины, и только она стоит часов.
wide назван по тому, что он добавляет: вход шире диффа. Он единственный, где
живут тяжёлые проходы, и единственный, где что-то запускается. basics в нём
берёт только проектные темы; своих тем у проекта нет — он не запускается вовсе, и
план говорит об этом строкой.
Доля 5–10% — не пожелание, а проверка правила. Если wide уходит каждая
третья задача, ступень выбирают по ощущению важности. Обратный перекос виден по
журналу проскочивших дефектов: класс, который ловят только меряющие проходы,
начинает всплывать после мерджа.
Состав сверяется до коммита — по плану разметчика, а не по этой таблице. План и есть реестр: тема, дом, глубина, кто закрывает. Это единственная защита от промаха, который уже случился: пропуск не отличим от прохода без находок (гейт зелёный, спеки сошлись, отчёт выглядит полным), а заметить его мог бы только триаж, который сам заполняется тем, что ему подали. Непущенное идёт строкой «не запускался» с причиной, а не отсутствует. Цена молчащего пропуска измерена: семь находок и отдельная задача на их дозакрытие.
Правило выбора — два вопроса по факту изменения, не по ощущению важности.
Дом правила здесь, а применяет его review-scope на стадии 0 — не автор
изменения. Отвечать по порядку, первый подошедший ответ и есть профиль:
- Изменение крупное или незнакомое? →
wide. Крупное — трогает несколько узлов или слоёв разом, переносит ответственность между ними, перекладывает существующий код в новую форму (большой рефакторинг). Незнакомое — функциональность, которой в проекте ещё не было, и форму решения предстоит нащупать по ходу, а не выбрать до начала. Признак незнакомого простой: перед работой нельзя назвать, какие узлы будут тронуты. - Изменение мелкое? →
quick. Мелкое — помещается в один узел, форма решения очевидна до начала работы, а откат сводится к обратной правке. Сюда идут мелкий багфикс, мелкая фича, правка текста и документации. - Всё остальное →
standard. Это рабочее умолчание, и оно должно набирать большинство задач.
Два признака смотрят на разное, и в этом весь смысл двух вопросов. Первый — про объём и неизвестность: сколько мест трогается и знаем ли мы форму решения заранее. Второй — про обратимость: во что обойдётся ошибка, если она уедет в мердж. Раньше ступень выбиралась только по классу изменения («вводит ли новое понятие»), и объём в правило не входил вовсе; теперь входит, потому что цена разбирательства растёт именно с ним.
Отрицательный тест quick, и он важнее положительного: изменение, которое
после мерджа не откатывается обратной правкой, — не quick, каким бы
маленьким ни был дифф. Сюда попадают миграция схемы и данных, формат на диске,
публичный контракт, имя, которое разойдётся по кодовой базе. Три строки миграции
— это standard, а не quick: размер диффа и цена ошибки здесь расходятся.
Что здесь считается крупным и что — незнакомым, проект может уточнить в
docs/review.md, разделе настройки конвейера: поимённо, узлами или capability.
Это уточнение, а не отмена: не записано — работает список выше.
Спорный случай решается вниз, и у этого есть цена
Правило асимметрично, потому что асимметрична цена ошибки.
- Спорно между
standardиwide→ бериstandard. Ошибка в эту сторону стоит находки, которая всплывёт на следующей задаче или в журнале дефектов. Ошибка в обратную стоит трёх тяжёлых проходов, двое из которых держат машину и идут цепочкой, — и платится она на каждой задаче, выбранной неверно. - Спорно между
quickиstandard→ бериstandard. Здесь состав тот же, и разница только в глубине трёх тем: сверка против разбора. Дёшево, и потому сомнение решается в пользу разбора.
Выбор сделан в пользу пропускной способности, и это записано, а не подразумевается. Конвейер настроен на поток задач, а не на максимум находок с каждой: поправить в следующей задаче дешевле, чем держать одну два часа. Отсюда три обязанности, без которых сделка превращается в незаметную потерю качества:
- границы покрытия называют темы и их глубину, а не только запущенные
проходы — иначе
quickвыглядит так же, какwideбез находок; - журнал дефектов в
docs/review.mdперестаёт быть хорошей практикой и становится единственной обратной связью: проскочивший дефект — единственный сигнал, что ступень выбрана слишком низко; - возврат в код — повод пересмотреть ступень. Задача, которая приходит в тот же узел третий раз, уже не мелкая, чем бы ни выглядел её дифф.
Профиль — максимум по поверхности
Условия читаются сверху вниз, и первое подошедшее отвечает за весь дифф.
Профиль изменения это максимум по его поверхности, а не средневзвешенное: одна
строка в перечне границ задачи поднимает ступень всему остальному, включая ту
часть, которая сама по себе была бы quick.
Обратное тоже верно и тоже не бесплатно: у каждой задачи есть несокращаемый
костяк из шести проходов (разметка, гейт, спеки, код, темы, триаж). Разрезать
задачу, обе половины которой остаются в одном профиле, — значит заплатить костяк
дважды за ту же проверку. Резать стоит там, где разрез снимает доказательство с
большей части диффа. Шов и правило нарезки живут у того, кто ведёт задачи, —
скилл av-dev-pm:tasks, его references/split.md. Пути туда конвейер не
выносит: за пределы своего плагина он ходит вызовом скилла, а не файлом.
Профиль и глубина объявляются в отчёте, и оба с обоснованием. Ступень
выбирает review-scope; он вправе и поднять, и понизить её — но не молча: строка
«ступень X, потому что …» обязательна на каждом прогоне, а не только когда
ступень отличается от ожидаемой.
Порядок прогона — граф, а не очередь
Профиль отвечает «какие темы и на какой глубине», порядок — «что кого ждёт». Стадии остаются единицей состава, но порядок задают не их номера: между стадиями 2–4 настоящих зависимостей нет — ни один проход не читает вывод другого, — и очередь между ними была бы платой ни за что.
Рёбер два вида, и они разной природы. Путать их нельзя: первое про осмысленность (без плана задание не определено, на красном гейте опиниативный проход не о чем), второе про железо.
| Ребро | Смысл | Между кем |
|---|---|---|
| зависимость | B не стартует, пока A не закончил, потому что без A задание B не определено | разметка → все; гейт → все опиниативные; все проходы → триаж |
| конфликт за ресурс | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» |
flowchart TD
scope["scope — разметка<br/>(стадия 0, темы и ступень)"]
autotests["autotests<br/>(стадия 1, держит машину)"]
specs["specs"]
code["code"]
basics["basics<br/>(quick, standard: темы;<br/>wide: только свои темы проекта)"]
adversary["adversary<br/>(wide, держит машину)"]
ops["ops<br/>(wide, держит машину)"]
architecture["architecture<br/>(wide)"]
triage["triage — единственный сток"]
scope -->|план| autotests
autotests -->|зелёный| specs
autotests -->|зелёный| code
autotests -->|"зелёный, темы по плану"| basics
autotests -->|"зелёный, wide"| adversary
autotests -->|"зелёный, wide"| ops
autotests -->|"зелёный, wide"| architecture
adversary -. один ресурс — машина .- ops
specs --> triage
code --> triage
basics --> triage
adversary --> triage
ops --> triage
architecture --> triage
Читается граф так: всё, у чего входящие рёбра закрыты, уходит одним
сообщением. Разметка идёт первой и одна — до неё неизвестно ни что проверять,
ни на какой ступени. В quick и standard после зелёного гейта уходят разом
specs, code и basics, и сразу триаж. В wide вместо тем basics идут три
тяжёлых: architecture и первый из меряющей пары — сразу, второй меряющий —
следом за первым, и он же определяет, когда стартует триаж.
Схема здесь старше прозы. Она не иллюстрация к тексту, а сам алгоритм планировщика; проза ниже объясняет рёбра и называет их цену. Разошлись — прав граф, а расхождение чинится правкой текста.
Ребро значит «A закончил раньше, чем B стартовал», и ничего больше. В обычном
графе задач ребро тянет за собой данные — здесь нет, и это не деталь реализации.
Проход не видит находок других проходов, в каком бы порядке их ни запустили.
Вся ценность конвейера держится на разведённости: под всеми ролями одна модель с
одними априорными, и стоит показать ей чужой вывод — она согласится. Согласие
нескольких проходов и так не повышает confidence (см. «Честный предел»);
согласие наведённое ещё и маскируется под независимое подтверждение.
Единственный, кто получает чужие выводы, — триаж, и это его работа.
Кто держит машину
Ресурс один и неделимый: машина — тесты, поднятый сервис, СУБД, порты, диск. Проходы, заявившие его, сериализуются между собой в любом профиле и на любой стадии; порядок внутри цепочки произволен.
| Проход | Держит машину | Почему |
|---|---|---|
autotests |
да | запускает инструменты проекта — но он источник графа и один по построению |
adversary |
да | находка есть построенный путь: он пишет падающий тест и гоняет его |
ops |
да | доказывает числами: время удержания блокировки, пик кучи, темп роста журнала |
triage |
да | проверяет оракул critical/major запуском — но он сток и тоже один |
scope, specs, code, basics, architecture, rubric |
нет | читают и рассуждают, ничего не исполняют |
Правило про ресурс, а не про имена. Раньше здесь стояло именованное
исключение «adversary и ops»; оно рассыпается, как только проход начнёт
мерить или в проекте появится свой. Два прохода на одной машине соревнуются за
диск, CPU и за саму СУБД и выдают числа, которые не воспроизведутся, — а число,
снятое под конкурентную нагрузку, это находка с испорченным оракулом. Её
опровержение стоит дороже всего выигрыша от параллельности, и она хуже
отсутствующей: выглядит доказанной. Правило выведено из находок, целиком
державшихся на таких замерах; у каждого проекта они свои и лежат в журнале
docs/review.md.
Проект вправе пометить «держит машину» и другой проход — в docs/review.md,
разделе настройки конвейера. Снимать пометку с перечисленных нельзя.
Находка «переделать форму» — прогон повторяется целиком
Барьера стоимости в конвейере нет, и раннего выхода тоже. Барьер существовал
ради независимой реализации — единственного прохода, чей счёт определялся объёмом
вывода, — и ушёл вместе с ней. Граф во всех профилях плоский, от гейта до триажа:
защищать за барьером нечего, architecture дёшев по выходу (потолок 3 находки), а
сериализация не бесплатна — она разводит по очереди то, что могло идти разом.
Находка «форму изменения надо переделывать» ловится триажем, как и любая другая; дальше правило одно. Находка чинится, и конвейер запускается заново с нулевой стадии, а не «доезжает» остатком по коду, которого через час не станет. Если прогон всё же остановлен на полпути, незапущенные проходы идут в границы покрытия строкой «не запускался: прогон остановлен на <проход> из-за <находка>», поимённо, а триаж на половине прогона не запускается: его отчёт выглядит полным, потому что агрегирует всё, что ему подали, — это тот же молчащий пропуск, что и в разделе «Профили».
Находка, которая чинится в пределах существующей формы (Действие: инлайн),
прогон не останавливает: дешевле дособрать все находки и починить пачкой, чем
гонять конвейер дважды.
Линеаризация — когда графа мало
Граф можно вытянуть в одну цепочку. Это отступление, и оно называется в отчёте:
- сказал оператор — «гони линейно». Набора называть не надо: линейный прогон ничего не портит, он только дольше, и домысливать тут нечего;
- машина занята, и знает об этом вызывающий. Рядом идёт другая задача,
поднят сервис, гоняется дорогая проверка проекта. Сам конвейер занятости
машины не видит — её обязан назвать тот, кто запускает; так и делает
av-dev-pipeline:task-batch, когда ведёт задачи параллельно; - разбор самого конвейера — когда выясняется, почему проход чего-то не нашёл, порядок и изоляция важнее скорости.
Обратное отступление — слить цепочку ресурса (пустить меряющие проходы разом) — бывает только по прямому слову оператора, и тогда в границы покрытия идёт строка: какие проходы шли одновременно и что замеры этого прогона как оракул слабее.
Режим объявляется в отчёте наравне с профилем: по графу — одним словом,
линейно — с причиной (какой именно из трёх).
Стадия 0 — Разметка (обязательна во всех профилях)
Агент review-scope. Идёт первым, до гейта, и один: до его плана неизвестно
ни что проверять, ни на какой ступени.
Возвращает план прогона: список тем с домами и глубинами, ступень с
обоснованием, перечень документов, не ставших темами, и строку про найденные
директивы (CLAUDE.md, AGENTS.md). План уезжает в отчёт целиком и служит
границами покрытия.
Он не судит по существу — ни одной находки об изменении. Его ошибка это
пропущенная тема или не та ступень, и обе видны: план сверяется с ls docs/ за
секунду, а заниженную ступень ловит basics своим сигналом.
Ступень выбирает он, а не автор изменения. Раньше профиль называл тот же оркестратор, который писал код: он же решал, насколько глубоко его проверять, — и разведённости с автором в этой точке не было вовсе. Вызывающий пайплайн профиль больше не передаёт; он передаёт change, базу диффа и режим.
Право у разметчика симметричное: поднять и понизить ступень он может одинаково, но обоснование обязательно в обоих случаях и всегда — строкой, какой признак сработал и по какому факту.
Стадия 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.
Технический разбор — не тема, а обязанность прохода, и он единственный.
Остальные читают код как материал для своей оптики: specs — против требований,
basics — против отказов окружения, architecture — против устройства. «Здесь
ошибка в логике» не говорит больше никто, и до недавнего времени не говорил
никто вовсе: code был проходом только по конвенциям, а дефект ловился разве что
случайно. Это была самая крупная дыра конвейера, и стоила она дороже любой
недосмотренной темы.
Recall темы conventions равен длине конвенций проекта — это предел любой
сверки, и ровно ради него существуют стадии 3 и 4.
Оба прохода на верхней модели, и по одной причине — цене пропуска. У specs
это направление code → spec: надо заметить отсутствие — тихий фолбэк,
самодеятельный дефолт, проглоченную ошибку. У code это пропущенный дефект,
который поедет в прод. Ни то ни другое не оставляет следа ни в отчёте, ни в
границах покрытия; прочие опиниативные проходы держат opus из-за цены ложных
находок, эти двое — из-за цены пропущенных.
Стадия 3 — Темы (quick, standard; в wide — только свои темы проекта)
Агент review-basics. Один проход, машину не держит, ничего не запускает и не
меряет — уходит одним сообщением вместе со стадией 2, сразу после зелёного гейта.
Он не самостоятельная оптика, а держатель тем, у которых на этой ступени нет
своего проходчика. На quick и standard это security, operations и
architecture: их именные проходы живут в wide, и без basics эти темы на
большинстве задач не смотрел бы никто. На любой ступени, включая wide, он же —
приёмник проектных тем: именных проходов конечное число, а тем столько,
сколько заведёт проект.
Глубина приходит из плана: сверка (открыть дом темы, открыть дифф, сравнить;
потолок 2 находки) или разбор (построить сценарий рассуждением; потолок 4).
Чего он не делает ни на какой глубине — замеров, эксперимента против драйвера,
построенного пути, карты проекта, границы домена. Всё это стоит машины или входа
шире диффа, то есть ровно того, ради чего существует wide.
Он покрывает миграцию и публичный контракт на нижних ступенях. Это не
побочный эффект, а условие, при котором миграция схемы вообще может не поднимать
ступень: её шаг гоняет autotests, спеку сверяет specs, а вопросы «обратима ли» и
«что с записями новой версии после отката» задаёт здесь basics, темой
operations. Уберёшь его — и нижние ступени останутся без единственного прохода,
который смотрит на ось времени.
В wide он запускается только при своих темах проекта. Нет таких — план
говорит строкой «basics не запускается: все темы разобраны именными проходами».
Это единственное место, где состав не выводится из профиля, и потому оно
называется в плане явно.
Стадия 4 — Доказательство (только wide)
Три прохода, и все три уходят сразу после зелёного гейта, в одном ряду со стадией 2. Каждый берёт свою тему и доводит её до доказательства:
review-adversary, темаsecurity— находка есть построенный путь, а не свойство: он пишет падающий тест и гоняет его;review-ops, темаoperations— постмортем от симптома у владельца сервиса к строке кода, с числами;review-architecture, темаarchitecture— концептуальная целостность на входе шире диффа.
Первые двое помечены «держит машину», поэтому между ними ребро конфликта: они
идут цепочкой, а не разом (правило и его причина — в «Порядок прогона», раздел
«Кто держит машину»). Направления у ребра нет: кто первый — неважно.
architecture машину не держит и ждать ему нечего — он уходит в первой волне.
Цепочка не отменяется общим «гони по графу» — она и есть часть графа. Отменяет её только прямое слово оператора про эту пару, и тогда в границы покрытия идёт строка, что числа прогона сняты под соседней нагрузкой.
Эта стадия зарабатывает больше всех остальных вместе — и она же дороже всех остальных вместе. Измерено на пяти задачах подряд: враждебный проход дал пять из семи выживших находок дозапуска (включая обе верхние); эксплуатационный — единственный, кто нашёл, что откат бинаря поверх новой схемы стартует молча. Оба несут внешний оракул по построению: один обязан путь прогнать, второй смотрит ось времени и эксплуатации. Ровно поэтому они и стоят денег: оракул добывается запуском, а запуск — это машина, цепочка и часы.
Раньше эта пара стояла в standard, то есть на большинстве задач. Стадия
переехала в wide сознательно и по цене, а не потому, что перестала находить:
она осталась самой ценной, но её ценность оплачивается на каждой задаче, а
получается — на немногих. Что из-за этого перестало проверяться на нижних
ступенях, названо в «Честном пределе» и обязано идти строкой в границы покрытия
каждого прогона quick и standard.
Дома тем приходят из плана разметчика: security — враждебному, operations
(эксплуатация, хранилище, числа) — эксплуатационному, architecture (устройство,
граница домена, решения) — архитектурному. Что с чем сшивать и почему —
project-facts.md, раздел «Сшивать обязаны
проходы». Без домов стадия вырождается в общие места.
Условие стадии и есть условие ступени wide: изменение крупное или
незнакомое. У архитектурного прохода работа появляется тогда, когда трогается
несколько узлов разом или в проекте становится больше сущностей, чем было; у
меряющей пары — когда форму решения нащупывали по ходу, и потому неизвестно, где
она протекает. На мелкой правке вопрос «не появился ли второй способ» отвечается
«нет» до запуска, а построенный путь строить негде.
review-architecture получает вход шире диффа: дерево пакетов с
назначением, граф внутренних зависимостей, инвентарь существующих концепций.
Команду, которая это готовит, даёт раздел команд CLAUDE.md; нет команды —
проход собирает карту сам и говорит об этом в границах покрытия.
Главный вопрос — концептуальная целостность и второй способ делать то, что уже делается. Он же и оправдывает проход: на задаче про пересборку архитектурный проход нашёл, что новый код был вторым проигрывателем журнала со своим порядком. Второй обязательный вопрос — что опытный человек отсюда удалил бы: слой с единственной реализацией, интерфейс ради мока, незапрошенная конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс секция «дешевле переделать до мерджа».
Стадия 5 — Triage (обязательна)
Агент review-triage. Единственный сток графа и единственный, кто агрегирует.
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
стартует. Получает сырые выводы всех проходов, git diff, режим и план
разметчика; возвращает финальный отчёт.
План на входе у триажа — не формальность, а сверка. Он единственный, кто видит и то, что размечено, и то, что пришло: «тем размечено шесть, отчёты покрывают пять» — находка о самом прогоне, и заметить её больше некому. Раньше он получал список запущенных проходов и потому мог сверить только состав; теперь сверяет темы, а тема, оставшаяся без отчёта, — это то, чего список проходов никогда не показывал.
Отсюда же правило, которое иначе выглядит придиркой: триаж на неполном графе не запускается. Прогон, остановленный на полпути находкой «переделать форму», до стока не доезжает — его отчёт агрегировал бы половину и выглядел бы полным.
Без триажа проходы дают порядка сорока замечаний при единицах существенных. Потребитель здесь — оркестратор, который молча реализует всё, что прочитал: цена нетриажированного отчёта — не потерянное время человека, а разросшийся от вкусовщины код.
Порядок: дедупликация по причине → оракул для всего critical/major →
понижение неподтверждённого до гипотезы → отсев вкусовщины → ранжирование по
ущербу × вероятности → потолок 7 пунктов в основном списке.
Профиль design — до кода
Запускается на шаге ревью спек (шаг 4 скилла av-dev-pipeline:task-pipeline),
когда change уже
имеет proposal.md и дельта-спеки, но кода ещё нет.
Состав здесь тоже не постоянный, и условие то же самое, что у wide:
изменение крупное или незнакомое.
- всегда —
review-specsв режиме «дизайн ДО кода». Дельта-спеки сверяются на каждой задаче: это самый дешёвый чекпоинт конвейера, и он ловит то, что на готовом коде уже не чинят; - при крупном или незнакомом — плюс
review-rubric(фаза 1 без фазы 2: рубрика на задуманный узел становится приёмочными критериями и уезжает вtasks.md) иreview-architectureна предложении: можно ли выразить существующими понятиями — включая конструкции стандартной библиотеки, — не появляется ли второй способ. Вопрос «не изобретаем ли то, что уже есть в библиотеке» живёт здесь; тогда же задаётся вопрос автору дизайна: «предложи три формы решения и назови компромисс каждой» — если ответ показывает, что рассматривалась одна, это находка.
Причина условия — арифметика, а не экономия на осторожности. Чекпоинт стоит
на каждой задаче, поэтому три прохода здесь умножаются на число задач, и при
мелкой нарезке это самая большая статья конвейера. Рубрика же на узел знакомого
рода порождает свойства уже существующего рода — те, что и так записаны
конвенциями и спеками; а architecture на мелкой правке отвечает «нет» на свой
главный вопрос ещё до запуска (см. «Стадия 4»).
Граф этого профиля свой, и он плоский. Гейта нет — кода ещё нет, запускать нечего; разметка сводится к одному вопросу (крупное или незнакомое), и его задаёт вызывающий вместе с профилем; машину не держит ни один проход; сток — не триаж, а шаг 5 пайплайна задачи, где замечания отрабатываются правкой спек. Триаж здесь не нужен: находок единицы, и каждая либо правит спеку, либо становится развилкой.
flowchart TD
proposal["предложение: proposal.md + дельта-спеки"]
specs["specs (режим «дизайн ДО кода») — всегда"]
novelty{{"изменение крупное<br/>или незнакомое?"}}
rubric["rubric, фаза 1 → приёмочные критерии в tasks.md"]
arch["architecture на предложении"]
author["вопрос автору: три формы решения и компромисс каждой"]
fix["шаг 5 пайплайна: правка спек, развилки — вопросом в запись"]
proposal --> specs
proposal --> novelty
novelty -->|да| rubric
novelty -->|да| arch
novelty -->|да| author
novelty -->|нет| fix
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.*, по темам, и оба его подраздела целиком уезжают в
границы покрытия. Тема, у которой нет дома, — тоже граница покрытия, и она
объявляется планом на каждом прогоне, а не разово.
Независимо от проекта недоступно:
- поведение внешних систем в их будущих версиях;
- реальный профиль нагрузки; и то, что на самом деле лежит в данных, — сверх
того, что снято с провенансом в
docs/research/; - завязка внешних потребителей на текущую форму ответа;
- суждение «этой функциональности не должно существовать».
Отдельно и честно: поимённая сверка с положениями руководств по стилю языка не
задаётся ни одним проходом. Проход про идиоматичность упразднён, его способные
части переселены (эксперимент против поведения библиотеки и драйвера — в ops,
вопрос 8; «не изобретаем ли то, что уже есть в библиотеке» — в architecture,
вопрос 1), но различение «идиоматично против распространено» теперь не спрашивает
никто. Класс обратимый — портит форму кода, не данные, — и его надо признавать в
границах покрытия, а не считать проверенным.
На quick и standard не проверяется ничего, что требует запуска. Это самая
крупная граница покрытия конвейера, и она обязана идти строкой в каждом таком
прогоне — поимённо, а не общим «профиль ниже». Не проверяется: построенный путь
атаки (его надо прогнать), поведение библиотеки и драйвера в вырожденном случае
(достаётся только экспериментом), любое число — время удержания блокировки, пик
кучи, темп роста журнала, стоимость на годовой истории. basics задаёт часть тех
же вопросов чтением, и его ответы поэтому слабее: он формулирует условиями,
оракула не приносит и выше гипотезы находку не поднимает — кроме той, что
опирается на инвариант CLAUDE.md.
Темы при этом закрыты все — разница в глубине, и её надо читать буквально.
«Тема security, глубина сверка» не значит «безопасность проверена»: значит, что
дом темы открыли, дифф посмотрели и сравнили. Между сверкой и доказательством
лежит весь класс дефектов, который виден только построенным путём, — и он
проверяется на 5–10% задач.
Это сознательная сделка, а не пробел в устройстве: цена ступени wide платится на
каждой задаче, а окупается на немногих. Проверяется сделка не рассуждением, а
журналом дефектов: если класс, который ловят только меряющие проходы, начал
всплывать после мерджа — ступень выбирают слишком низко.
Так же честно и про упразднённый проход: «не знаю, чего не знаю» больше
не достаёт никто. Проход независимой реализации писал свою версию узла, не
открывая существующую, и диффил по решениям — декомпозиция, владение данными,
модель конкурентности, форма решения там, где спека выбора не сделала. Он снят по
решению оператора о стоимости — счёт определялся объёмом вывода, и на прогон
он тратил больше всех остальных проходов вместе, — а не по замеру, который
calibration.md требует перед удалением. Значит и
записывается это как сознательное сужение, а не как «класс оказался пустым»:
остаток независимого взгляда даёт профиль design (код пишется под его находки) и
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 — журнал проскочивших дефектов.