Files
avandClaude Opus 5 cbfae90f3f словарь: пять слов сняты, девять закрыты списком вместо оговорки «прижилось»
Проход упрощения уткнулся в один класс у всех пяти агентов: слово, живущее в
трёх-шести файлах разом. Правка в одном месте развела бы словарь, правка во
всех — уже не упрощение текста скилла. Каждый честно остановился и записал слово
в отчёт, и одни и те же слова всплыли в разных отчётах. Разобрано этим проходом.

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

Список заведён домом язык-словарь в language.md и копией в уставе doc-wording.
Копия обязательна: агент работает в репозитории проекта, где плагина может не
быть, и без списка предъявил бы интейк как англицизм.

Снято пять слов, 29 мест: конфляция → смешение, декорреляция → разведённость,
непоймание → почему не поймали, эвал-сет → проверочный набор, гайд →
руководство. Латинизм или калька при живом русском слове в каждом случае.

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

Непоймание снято ещё и потому, что форма журнала дефектов, которую канон кладёт
в проекты, спрашивает «Почему не поймали», а проза рядом называла это «причиной
непоймания». Скелет и проза о скелете говорили разными словами.

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

Тема 32 в DECISIONS.md, следствия 124-126. Нумерация правил в уставе doc-wording
сдвинута: словарь встал шестым, жаргон и далее уехали на единицу.

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

66 KiB
Raw Permalink Blame History

name, description
name description
review-pipeline Конвейер ревью изменения — детерминированный гейт, сверка с дельта-спеками в обе стороны, враждебные постановки и эксплуатационный постмортем, архитектурный проход, независимая реализация в верхнем профиле и обязательный триаж. Четыре ступени стоимости: quick, standard, wide, deep. Порядок прогона — граф зависимостей, а не очередь: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, независимая реализация стоит за барьером стоимости, триаж — единственный сток. Линейный прогон — по слову оператора или на занятой машине. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода.

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

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

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

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

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

Предпосылки

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

  • 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.

Что конвейер защищает — приходит из документов проекта

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

Карта «что нужно проходу → где лежит» — references/project-facts.md. Прочитай её до раздачи заданий; там же таблица поразрядной деградации.

Деградация поразрядная, а не всё-или-ничего. Документа нет — деградирует то, что из него читалось, и только оно: нет docs/security.md — слабеет adversary; нет docs/research/ — числа неизвестны трём проходам; нет инвариантов в CLAUDE.mdcritical по основанию «нарушен инвариант проекта» не присваивается никем. Каждый проход пишет свою строку в границы покрытия, с причиной; триаж сводит их и не сливает в одну.

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

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

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

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

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

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

Следует из правила 2: чем больше работы делает детерминированный инструмент, тем дешевле может быть модель; чем больше проход порождает критерий, тем дороже. Модель задана во frontmatter каждого агента, менять её здесь не нужно.

Модель Цвет Проходы Почему
sonnet green gate, code, ops вход структурный, критерий записан заранее
opus yellow specs, adversary, rubric, reimpl суждение без опоры на инструмент
fable red triage, architecture ошибка распространяется дальше самой находки

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

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

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

  • triage — через него проходит всё, что оркестратор реализует молча: ложноположительная находка становится кодом, потерянный critical — дефектом. Ошибка триажа дороже ошибки любого отдельного прохода.
  • architecture — запускается только там, где изменение вводит новое понятие, потолок в 3 находки делает его дешёвым по выходу, а находка на предложении стоит абзаца против переписывания на готовом коде. Дёшево × высокое плечо.

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

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

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

Профили

Профиль Когда Стадии Проходов
quick багфикс, локальная правка, доки 0, 1, 5 4
standard рабочее умолчание: поведение, миграция схемы, публичный контракт, инвариант 0, 1, 2, 5 6
wide изменение вводит новое понятие или структурную единицу 0, 1, 2, 4, 5 7
deep изменение вводит новое правило идентичности, слияния или разбора 0, 1, 2, 3, 4, 5 8
design до кода, на предложении specs, плюс rubric и architecture по условию wide 13

wide назван по тому, что он добавляет: вход шире диффа. Единственное его отличие от standard — архитектурный проход, а тот и получает дерево пакетов, граф зависимостей и инвентарь понятий вместо одного диффа. Ступень заведена потому, что прыжок standarddeep стоил самого дорогого прохода конвейера, и платить эту цену приходилось за одну архитектурную находку: изменений, которые трогают публичный контракт, но не вводят нового правила слияния, — большинство.

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

Правило выбора профиля — по факту изменения, не по ощущению важности:

  • вводится или меняется по существу правило, определяющее идентичность, слияние или разбор данных (тест — ниже) → deep;
  • иначе изменение вводит новое понятие или структурную единицу: новый пакет или слой, новая точка входа, второй способ делать то, что уже делается, перенос ответственности между узлами → wide;
  • иначе меняется поведение, видимое снаружи, трогается схема, публичный контракт или инвариант проекта → standard;
  • иначе → quick.

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

Оно же объясняет, почему миграция схемы и публичный контракт не поднимают ступень, хотя выглядят опаснее прочего. Они не добавляют ни одного прохода: миграцию гоняет gate шагом миграций и разбирает ops («миграция под живым потоком», «частичный откат при двух версиях»), контракт сверяет specs направлением code → spec, инвариант даёт основание для critical любому проходу. Все трое уже в standard. Раньше эти три факта стояли триггерами верхних ступеней, и на проекте с базой и эндпоинтами верхняя ступень оказывалась не исключением, а умолчанием — то есть правило объявляло исключением то, что происходит всегда. architecture же получает работу не от того, что контракт изменился, а от того, что появилось новое понятие: добавленное поле в существующем ответе — не концепт.

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

Что здесь считается новым понятием и что — правилом идентичности, проект может уточнить в docs/review.md, разделе настройки конвейера. Это уточнение, а не отмена: не записано — работает список выше. Проект, где изменение контракта и правда архитектурное (публичный SDK, чужие потребители), там же поднимает его до wide — и это уточнение, а не возврат прежнего умолчания.

Идентичность, слияние, разбор — тест, а не список

Три слова названы затем, чтобы верхнюю ступень нельзя было выбрать по ощущению. Читаются они тестом, применимым к любому проекту на любом языке; домен, стек и имена узлов в тест не входят.

Правило принадлежит этому классу, если сходятся три условия:

  1. вариантов несколько — два добросовестных исполнителя выберут разное, и оба решения защитимы;
  2. спека между ними не выбирает — она требует, чтобы вещи сравнивались, сливались или разбирались, но не называет исход в пограничном случае;
  3. неверный выбор не падает — он даёт правдоподобный результат и меняет смысл данных молча.

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

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

Три слова — это три места, где такие правила водятся, и все три стоят на границе, где данные входят или встречаются:

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

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

Ступень определяет класс правила, а не вид работы. Миграция схемы сама по себе standard — но миграция, которая переносит данные по правилу («сложить дубли», «привести к одному виду перед сравнением»), несёт правило идентичности и потому deep. Одно и то же слово в описании задачи попадает в разные ступени, и это не противоречие: смотрят не на слово, а на то, есть ли выбор, которого спека не сделала.

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

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

Профиль — максимум по поверхности, и отсюда размер задачи

Условия читаются сверху вниз, и первое подошедшее отвечает за весь дифф. Профиль изменения это максимум по его поверхности, а не средневзвешенное: одна строка в перечне границ задачи поднимает ступень всему остальному, включая ту часть, которая сама по себе была бы quick.

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

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

Профиль объявляется в отчёте. Понижение профиля — решение оркестратора, и оно попадает в границы покрытия строкой «профиль понижен до X, потому что …».

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

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

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

Ребро Смысл Между кем
зависимость B не стартует, пока A не закончил, потому что без A задание B не определено гейт → все опиниативные; все проходы → триаж
конфликт за ресурс A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет проходы, помеченные «держит машину»
барьер стоимости дорогое не запускается, пока дешёвое не сказало, что форма изменения выживет только deep
flowchart TD
    gate["gate<br/>(стадия 0, держит машину)"]
    specs["specs"]
    code["code"]
    adversary["adversary<br/>(держит машину)"]
    ops["ops<br/>(держит машину)"]
    architecture["architecture<br/>(wide, deep)"]
    barrier{{"форма изменения выживает?"}}
    reimpl["reimpl"]
    triage["triage — единственный сток"]

    gate -->|зелёный| specs
    gate -->|зелёный| code
    gate -->|зелёный| adversary
    gate -->|зелёный| ops
    gate -->|"зелёный, wide и deep"| architecture
    adversary -. один ресурс — машина .- ops
    specs --> barrier
    code --> barrier
    adversary --> barrier
    ops --> barrier
    barrier -->|"deep"| reimpl
    barrier -->|"quick, standard, wide: барьера нет"| triage
    reimpl --> triage
    architecture --> triage

Читается граф так: всё, у чего входящие рёбра закрыты, уходит одним сообщением. В standard после зелёного гейта это три узла разом — specs, code и первый из меряющей пары, — а второй меряющий идёт следом за первым. В wide к этой тройке добавляется четвёртым architecture. В quickspecs и code разом, и сразу триаж.

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

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

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

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

Проход Держит машину Почему
gate да запускает инструменты проекта — но он источник графа и один по построению
adversary да находка есть построенный путь: он пишет падающий тест и гоняет его
ops да доказывает числами: время удержания блокировки, пик кучи, темп роста журнала
triage да проверяет оракул critical/major запуском — но он сток и тоже один
specs, code, reimpl, architecture, rubric нет читают и рассуждают; reimpl пишет свою реализацию в черновик, но не исполняет её

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

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

Барьер стоимости — вместо раннего выхода

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

  • прошло без находок «переделать форму» — барьер открыт, reimpl уходит;
  • есть такая находка — прогон останавливается, находка чинится, конвейер запускается заново с нулевой стадии, а не «доезжает» остатком по старому коду. Незапущенные проходы идут в границы покрытия строкой «не запускался: прогон остановлен на <проход> из-за <находка>», поимённо. Триаж на половине прогона не запускается: его отчёт выглядит полным, потому что агрегирует всё, что ему подали, — это тот же молчащий пропуск, что и в разделе «Профили»;
  • находка чинится в пределах существующей формы (Действие: инлайн) — барьер не срабатывает: дешевле дособрать все находки и починить пачкой, чем гонять конвейер дважды.

architecture стоит за барьером только там, где барьер и так есть. В deep он уходит вместе с reimpl — ждать ему всё равно нечего. В wide он стартует сразу после зелёного гейта, в одном ряду со стадиями 1 и 2: своего барьера он не заслуживает. Потолок в 3 находки делает его дешёвым, а барьер не бесплатен — он сериализует то, что могло идти разом, и платить сериализацией за один дешёвый проход не за что. Есть и вторая причина, помельче: барьер спрашивает «выживает ли форма изменения», а architecture — как раз тот, кто на этот вопрос отвечает.

В quick, standard и wide барьера нет — за ним нечего защищать: стадии 3 в этих профилях не бывает, и граф там плоский от гейта до триажа. Находка «переделать форму» ловится в них триажем, а прогон после починки повторяется целиком: платить за это нечем, дорогих проходов в этих профилях нет. В design его тоже нет, и по другой причине: там предметом и является форма, а все три прохода читают одно предложение — защищать нечего, у графа этого профиля своя форма (см. его раздел).

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

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

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

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

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

Стадия 0 — Gate (обязательна во всех профилях)

Агент review-gate. Запускает команду гейта из семантики гейта в CLAUDE.md и интерпретирует вывод.

Пока гейт красный — опиниативные проходы не запускаются. Оркестратор чинит и перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки (гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не блокирует.

Гейт возвращает не только «зелено/красно», но и находки класса отсутствующая верификация: изменённые строки без покрытия, конкурентность без теста с параллельным доступом, флаки-тест (не ниже major), недоступный инструмент.

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

Шаги, которые красят гейт безусловно, перечислены в CLAUDE.md с причиной. Проходу запрещено списывать такой отказ в мелочь.

Стадия 1 — Conformance (обязательна во всех профилях)

Два applicative-прохода: оба применяют записанный критерий, оба дешёвые. Машину не держат ни один, ребра между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со стадией 2, если она в профиле.

  • review-specs — критерий взят из дельта-спек предлагаемого изменения, а не из proposal, сообщения коммита или описания задачи. Сверка двунаправленная; направление code → spec важнее.
  • review-code — критерий взят из конвенций проекта, каталог docs/conventions/. Берётся только та их часть, которая не выражается правилом: механизируемое уже проверила стадия 0. Что именно механизировано, перечисляет conventions/README.md — повторять это проходом вредно.

Recall обоих равен длине их источника — это и есть предел applicative-проходов, ради которого существует стадия 2.

Стадия 2 — Adversarial и operational (standard, wide, deep)

Два прохода:

  • review-adversary — находка есть построенный путь, а не свойство;
  • review-ops — постмортем от симптома у владельца сервиса к строке кода.

Оба помечены «держит машину», поэтому между ними ребро конфликта: они идут цепочкой, а не разом (правило и его причина — в «Порядок прогона», раздел «Кто держит машину»). Направления у ребра нет: кто первый — неважно. Со стадией 1 они конфликта не имеют и стартуют одновременно с ней; ждать её незачем.

Цепочка не отменяется общим «гони по графу» — она и есть часть графа. Отменяет её только прямое слово оператора про эту пару, и тогда в границы покрытия идёт строка, что числа прогона сняты под соседней нагрузкой.

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

Материал берётся из документов: docs/security.md — враждебному, docs/architecture.md, docs/research/ и docs/database.md — эксплуатационному. Что с чем сшивать и почему — project-facts.md, раздел «Сшивать обязаны проходы». Без этих документов стадия вырождается в общие места.

Стадия 3 — Independent reimplementation (только deep)

Единственный проход, ради которого существует барьер стоимости, и единственное, что отличает deep от wide.

  • review-reimpl — пишет свою реализацию, не открывая существующую, затем диффит по решениям. Профиль и есть его условие: deep выбирается ровно тогда, когда вводится или меняется по существу правило идентичности, слияния или разбора — по тесту из раздела «Идентичность, слияние, разбор»; проектный перечень мест, где такие правила живут, — в docs/review.md, если записан. Это самый дорогой проход конвейера (его счёт определяется объёмом вывода — он пишет реализацию целиком), а вне этого случая независимый взгляд в значительной мере уже дал профиль design: код писался под его находки. Условие выбрано по факту: единственный раз, когда триаж назвал отсутствие reimpl дырой покрытия, — это была задача с новым правилом слияния сущностей.

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

Раньше это условие стояло внутри профиля, и deep означал то семь проходов, то восемь. Реестр состава, который «проверяется взглядом», проверять было нечем: у профиля не было одного правильного ответа. Теперь ступеней две — wide и deep, — и у каждой состав ровно один.

Стадия 4 — Global (wide, deep, design)

Агент review-architecture. В deep стоит за барьером стоимости (ждать ему там всё равно нечего), в wide и design — в первой волне, сразу после старта профиля. Машину не держит, с reimpl конфликта не имеет: за барьером они уходят разом.

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

Получает вход шире диффа: дерево пакетов с назначением, граф внутренних зависимостей, инвентарь существующих концепций. Команду, которая это готовит, даёт раздел команд 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.md, и оба его подраздела целиком уезжают в границы покрытия. Независимо от проекта недоступно:

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

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

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

Ссылки

  • 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 — журнал проскочивших дефектов.