порядок проходов ревью — граф, а не номера стадий
Номер стадии не означал зависимости: между стадиями 1–4 ни один проход не читает вывод другого, и очередь между ними была платой ни за что. А правило про замеры держалось на двух именах и рассыпалось бы в день, когда мерить начнёт третий проход. Рёбер три вида, и они разной природы: зависимость (гейт → опиниативные, все проходы → триаж), конфликт за ресурс (ненаправленный) и барьер стоимости. Стадии остаются единицей состава, порядок задаёт граф: уходит всё, у чего входящие рёбра закрыты. Сериализует ресурс, а не имена: пометка «держит машину» — gate, adversary, ops, triage; остальные читают и рассуждают. Проект вправе пометить свой проход, снять пометку с перечисленных — нет. Ранний выход заменён барьером стоимости и стоит там, где выход зарабатывал: перед reimpl и architecture, то есть только в deep. Отдельным абзацем — что ребро значит порядок и никогда не данные: графовый словарь провоцирует обратное прочтение, а проход, увидевший чужие находки, соглашается с ними. Исключение одно и оно же сток. Диаграммы — mermaid, прогнаны через mermaid-cli. Режим прогона теперь «по графу» / «линейно»; task-batch и task-pipeline подтянуты под общий словарь, adversary и ops знают о пометке из своих charter'ов. DECISIONS 15. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: review-pipeline
|
||||
description: Конвейер ревью изменения — детерминированный гейт, сверка с дельта-спеками в обе стороны, враждебные постановки и эксплуатационный постмортем, независимая реализация по триггеру, архитектура и обязательный триаж. Проходы внутри стадии гонятся параллельно; последовательно — по особой причине (меряющая пара, занятая машина) или по слову оператора. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода.
|
||||
description: Конвейер ревью изменения — детерминированный гейт, сверка с дельта-спеками в обе стороны, враждебные постановки и эксплуатационный постмортем, независимая реализация по триггеру, архитектура и обязательный триаж. Порядок прогона — граф зависимостей, а не очередь: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, дорогие generative-проходы стоят за барьером стоимости, триаж — единственный сток. Линейный прогон — по слову оператора или на занятой машине. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода.
|
||||
---
|
||||
|
||||
# Конвейер ревью
|
||||
@@ -95,7 +95,7 @@ description: Конвейер ревью изменения — детермин
|
||||
- **сужение**, если оно есть: конкретный узел, конкретная capability.
|
||||
|
||||
Чего проход **не** получает ни в каком режиме — выводов других проходов. См.
|
||||
«Режим запуска».
|
||||
«Порядок прогона».
|
||||
|
||||
## Модель по проходу
|
||||
|
||||
@@ -176,81 +176,139 @@ description: Конвейер ревью изменения — детермин
|
||||
Профиль объявляется в отчёте. Понижение профиля — решение оркестратора, и оно
|
||||
попадает в границы покрытия строкой «профиль понижен до X, потому что …».
|
||||
|
||||
## Режим запуска: параллельно или последовательно
|
||||
## Порядок прогона — граф, а не очередь
|
||||
|
||||
Профиль отвечает «какие проходы», режим — «как их запускать». Стадии всегда идут
|
||||
по порядку номеров; выбор касается только проходов **внутри** стадии.
|
||||
Профиль отвечает «какие проходы», порядок — «что кого ждёт». Стадии остаются
|
||||
единицей **состава** (профиль набирается стадиями, см. таблицу выше), но порядок
|
||||
задают **не их номера**: между стадиями 1–4 настоящих зависимостей нет — ни один
|
||||
проход не читает вывод другого, — и очередь между ними была бы платой ни за что.
|
||||
|
||||
| Режим | Как | Когда |
|
||||
Рёбер три вида, и они разной природы. Путать их нельзя: первое про
|
||||
**осмысленность** (на красном гейте опиниативный проход не о чем), второе про
|
||||
**железо**, третье про **деньги**.
|
||||
|
||||
| Ребро | Смысл | Между кем |
|
||||
|---|---|---|
|
||||
| **параллельно** (умолчание) | проходы стадии — одним сообщением | всегда, пока не сработала особая причина |
|
||||
| **последовательно** | по одному, следующий стартует после отчёта предыдущего | по слову оператора **или** по одной из особых причин ниже |
|
||||
| **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | гейт → все опиниативные; все проходы → триаж |
|
||||
| **конфликт за ресурс** | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» |
|
||||
| **барьер стоимости** | дорогое не запускается, пока дешёвое не сказало, что форма изменения выживет | только `deep` |
|
||||
|
||||
**Умолчание — параллельно, и его не надо обосновывать.** Обосновывается
|
||||
отступление. Проходы независимы по построению: ни один не видит выводов другого
|
||||
(см. ниже), у всех общий вход и разные критерии. Очередь между ними не добавляет
|
||||
ничего, кроме ожидания, — а ожидание на каждом чекпоинте ревью платится каждой
|
||||
задачей.
|
||||
```mermaid
|
||||
flowchart TD
|
||||
gate["gate<br/>(стадия 0, держит машину)"]
|
||||
specs["specs"]
|
||||
code["code"]
|
||||
adversary["adversary<br/>(держит машину)"]
|
||||
ops["ops<br/>(держит машину)"]
|
||||
barrier{{"форма изменения выживает?"}}
|
||||
reimpl["reimpl<br/>(по триггеру)"]
|
||||
architecture["architecture"]
|
||||
triage["triage — единственный сток"]
|
||||
|
||||
**Последовательно гоним в трёх случаях, и каждый называется в отчёте:**
|
||||
gate -->|зелёный| specs
|
||||
gate -->|зелёный| code
|
||||
gate -->|зелёный| adversary
|
||||
gate -->|зелёный| ops
|
||||
adversary -. один ресурс — машина .- ops
|
||||
specs --> barrier
|
||||
code --> barrier
|
||||
adversary --> barrier
|
||||
ops --> barrier
|
||||
barrier -->|"deep"| reimpl
|
||||
barrier -->|"deep"| architecture
|
||||
barrier -->|"quick, standard: барьера нет"| triage
|
||||
reimpl --> triage
|
||||
architecture --> triage
|
||||
```
|
||||
|
||||
1. **сказал оператор** — «гони последовательно». Набор при этом называть не
|
||||
обязательно: последовательный прогон ничего не портит, он только дольше, и
|
||||
домысливать тут нечего;
|
||||
2. **проходы меряют.** `adversary` и `ops` доказывают находки числами: время
|
||||
удержания блокировки против её таймаута, пик кучи против размера тела, темп
|
||||
роста файлов журнала, длительность транзакции. Два меряющих прохода на одной
|
||||
машине соревнуются за диск, CPU и за саму СУБД и выдают числа, которые не
|
||||
воспроизведутся. Это не гипотеза: правило выведено из находок, целиком
|
||||
державшихся на таких замерах, — у каждого проекта они свои и лежат в журнале
|
||||
`docs/review.md`. Число, снятое под конкурентную нагрузку от соседнего
|
||||
прохода, — это находка с испорченным оракулом, а её опровержение стоит дороже
|
||||
всего выигрыша от параллельности. **Эта пара идёт по очереди всегда** — это
|
||||
правило стадии 2, а не решение прогона (см. её раздел);
|
||||
3. **машина занята, и знает об этом вызывающий.** Рядом идёт другая задача,
|
||||
поднят сервис, гоняется гейт или дорогая проверка проекта. Сам конвейер
|
||||
занятости машины не видит — её обязан назвать тот, кто запускает; так и
|
||||
делает `av-dev-pipeline:task-batch`, когда ведёт задачи параллельно.
|
||||
Читается граф так: **всё, у чего входящие рёбра закрыты, уходит одним
|
||||
сообщением**. В `standard` после зелёного гейта это три узла разом — `specs`,
|
||||
`code` и первый из меряющей пары, — а второй меряющий идёт следом за первым. В
|
||||
`quick` — `specs` и `code` разом, и сразу триаж.
|
||||
|
||||
Отдельная причина, не связанная со стоимостью, — **разбор самого конвейера**:
|
||||
когда выясняется, почему проход чего-то не нашёл, порядок и изоляция важнее
|
||||
скорости.
|
||||
**Ребро значит «A закончил раньше, чем B стартовал», и ничего больше.** В обычном
|
||||
графе задач ребро тянет за собой данные — здесь нет, и это не деталь реализации.
|
||||
Проход **не видит** находок других проходов, в каком бы порядке их ни запустили.
|
||||
Вся ценность конвейера держится на декорреляции: под всеми ролями одна модель с
|
||||
одними априорными, и стоит показать ей чужой вывод — она согласится. Согласие
|
||||
нескольких проходов и так не повышает `confidence` (см. «Честный предел»);
|
||||
согласие **наведённое** ещё и маскируется под независимое подтверждение.
|
||||
Единственный, кто получает чужие выводы, — триаж, и это его работа.
|
||||
|
||||
Если меряющие проходы всё же пошли разом — а это бывает только по прямому слову
|
||||
оператора, — в границы покрытия идёт строка: какие проходы шли одновременно и
|
||||
что замеры этого прогона как оракул слабее.
|
||||
### Кто держит машину
|
||||
|
||||
**Чего режим не меняет — и это не подлежит обсуждению.** Проход **не видит**
|
||||
находок других проходов ни в каком режиме. «Последовательно» значит «по
|
||||
очереди», а не «следующий читает предыдущего». Вся ценность конвейера держится
|
||||
на декорреляции: под всеми ролями одна модель с одними априорными, и стоит
|
||||
показать ей чужой вывод — она согласится. Согласие нескольких проходов и так не
|
||||
повышает `confidence` (см. «Честный предел»); согласие **наведённое** ещё и
|
||||
маскируется под независимое подтверждение. Единственный, кто видит всё, — триаж,
|
||||
и это его работа.
|
||||
Ресурс один и неделимый: **машина** — тесты, поднятый сервис, СУБД, порты, диск.
|
||||
Проходы, заявившие его, сериализуются между собой в любом профиле и на любой
|
||||
стадии; порядок внутри цепочки произволен.
|
||||
|
||||
**Ранний выход — по границе стадии.** Стадии идут по порядку в любом режиме,
|
||||
поэтому остановиться между ними можно всегда; последовательный режим добавляет к
|
||||
этому возможность остановиться **внутри** стадии — это его побочная выгода, а не
|
||||
повод его выбирать. Допустимо остановить прогон, не докатив остаток, ровно в
|
||||
одном случае: находка требует **переделки формы** изменения, и
|
||||
остальные проходы будут смотреть на код, которого через час не станет. Тогда:
|
||||
| Проход | Держит машину | Почему |
|
||||
|---|---|---|
|
||||
| `gate` | да | запускает инструменты проекта — но он источник графа и один по построению |
|
||||
| `adversary` | да | находка есть **построенный путь**: он пишет падающий тест и гоняет его |
|
||||
| `ops` | да | доказывает числами: время удержания блокировки, пик кучи, темп роста журнала |
|
||||
| `triage` | да | проверяет оракул `critical`/`major` запуском — но он сток и тоже один |
|
||||
| `specs`, `code`, `reimpl`, `architecture`, `rubric` | нет | читают и рассуждают; `reimpl` пишет свою реализацию в черновик, но не исполняет её |
|
||||
|
||||
- прогон останавливается, находка чинится, конвейер запускается **заново с
|
||||
нулевой стадии** — а не «доезжает» остатком по старому коду;
|
||||
- незапущенные проходы идут в границы покрытия строкой «не запускался: прогон
|
||||
остановлен на <проход или стадия> из-за <находка>», поимённо;
|
||||
- триаж запускается только на полном прогоне. Отчёт триажа по половине проходов
|
||||
выглядит полным, потому что агрегирует всё, что ему подали, — это тот же
|
||||
молчащий пропуск, что и в разделе «Профили».
|
||||
**Правило про ресурс, а не про имена.** Раньше здесь стояло именованное
|
||||
исключение «`adversary` и `ops`»; оно рассыпается, как только проход начнёт
|
||||
мерить или в проекте появится свой. Два прохода на одной машине соревнуются за
|
||||
диск, CPU и за саму СУБД и выдают числа, которые не воспроизведутся, — а число,
|
||||
снятое под конкурентную нагрузку, это находка с испорченным оракулом. Её
|
||||
опровержение стоит дороже всего выигрыша от параллельности, и она хуже
|
||||
отсутствующей: выглядит доказанной. Правило выведено из находок, целиком
|
||||
державшихся на таких замерах; у каждого проекта они свои и лежат в журнале
|
||||
`docs/review.md`.
|
||||
|
||||
Ранний выход по находке, которая чинится в пределах существующей формы
|
||||
(`Действие: инлайн`), **не делается**: дешевле дособрать все находки и починить
|
||||
пачкой, чем гонять конвейер дважды.
|
||||
Проект вправе пометить «держит машину» и другой проход — в `docs/review.md`,
|
||||
разделе настройки конвейера. Снимать пометку с перечисленных нельзя.
|
||||
|
||||
Режим объявляется в отчёте наравне с профилем. Параллельный — одним словом.
|
||||
**Последовательный — с причиной** (какой именно из трёх) и с составом, если по
|
||||
очереди шла только часть проходов.
|
||||
### Барьер стоимости — вместо раннего выхода
|
||||
|
||||
Барьер существует ровно там, где ранний выход зарабатывал: `reimpl` пишет
|
||||
реализацию целиком и потому самый дорогой проход конвейера, `architecture`
|
||||
смотрит вход шире диффа. Если дешёвая часть нашла, что **форму изменения** надо
|
||||
переделывать, оба будут читать код, которого через час не станет.
|
||||
|
||||
- **прошло без находок «переделать форму»** — барьер открыт, дорогие проходы
|
||||
уходят разом;
|
||||
- **есть такая находка** — прогон останавливается, находка чинится, конвейер
|
||||
запускается **заново с нулевой стадии**, а не «доезжает» остатком по старому
|
||||
коду. Незапущенные проходы идут в границы покрытия строкой «не запускался:
|
||||
прогон остановлен на <проход> из-за <находка>», поимённо. Триаж на половине
|
||||
прогона не запускается: его отчёт выглядит полным, потому что агрегирует всё,
|
||||
что ему подали, — это тот же молчащий пропуск, что и в разделе «Профили»;
|
||||
- **находка чинится в пределах существующей формы** (`Действие: инлайн`) —
|
||||
барьер не срабатывает: дешевле дособрать все находки и починить пачкой, чем
|
||||
гонять конвейер дважды.
|
||||
|
||||
В `quick` и `standard` барьера нет — за ним нечего защищать: стадий 3–4 в этих
|
||||
профилях не бывает, и граф там плоский от гейта до триажа. Находка «переделать
|
||||
форму» ловится в них триажем, а прогон после починки повторяется целиком: платить
|
||||
за это нечем, дорогих проходов в этих профилях нет. В `design` его тоже
|
||||
нет, и по другой причине: там предметом и является форма, а все три прохода
|
||||
читают одно предложение — защищать нечего, у графа этого профиля своя форма (см.
|
||||
его раздел).
|
||||
|
||||
### Линеаризация — когда графа мало
|
||||
|
||||
Граф можно вытянуть в одну цепочку. Это отступление, и оно называется в отчёте:
|
||||
|
||||
1. **сказал оператор** — «гони линейно». Набора называть не надо: линейный прогон
|
||||
ничего не портит, он только дольше, и домысливать тут нечего;
|
||||
2. **машина занята, и знает об этом вызывающий.** Рядом идёт другая задача,
|
||||
поднят сервис, гоняется дорогая проверка проекта. Сам конвейер занятости
|
||||
машины не видит — её обязан назвать тот, кто запускает; так и делает
|
||||
`av-dev-pipeline:task-batch`, когда ведёт задачи параллельно;
|
||||
3. **разбор самого конвейера** — когда выясняется, почему проход чего-то не
|
||||
нашёл, порядок и изоляция важнее скорости.
|
||||
|
||||
Обратное отступление — **слить цепочку ресурса** (пустить меряющие проходы
|
||||
разом) — бывает только по прямому слову оператора, и тогда в границы покрытия
|
||||
идёт строка: какие проходы шли одновременно и что замеры этого прогона как
|
||||
оракул слабее.
|
||||
|
||||
Режим объявляется в отчёте наравне с профилем: **`по графу`** — одним словом,
|
||||
**`линейно`** — с причиной (какой именно из трёх).
|
||||
|
||||
## Стадия 0 — Gate (обязательна во всех профилях)
|
||||
|
||||
@@ -276,8 +334,8 @@ description: Конвейер ревью изменения — детермин
|
||||
## Стадия 1 — Conformance (обязательна во всех профилях)
|
||||
|
||||
Два applicative-прохода: оба применяют **записанный** критерий, оба дешёвые.
|
||||
Замеров они не делают и не мешают друг другу ничем — это канонический случай
|
||||
умолчания: оба уходят одним сообщением.
|
||||
Машину не держат ни один, ребра между ними нет — уходят одним сообщением сразу
|
||||
после зелёного гейта, вместе со стадией 2, если она в профиле.
|
||||
|
||||
- `review-specs` — критерий взят из **дельта-спек предлагаемого изменения**, а не
|
||||
из proposal, сообщения коммита или описания задачи. Сверка двунаправленная;
|
||||
@@ -297,13 +355,14 @@ Recall обоих равен длине их источника — это и е
|
||||
- `review-adversary` — находка есть **построенный путь**, а не свойство;
|
||||
- `review-ops` — постмортем от симптома у владельца сервиса к строке кода.
|
||||
|
||||
**Эта пара — именованное исключение из умолчания: она идёт по очереди всегда.**
|
||||
Оба доказывают находки замером, и оба меряют одно и то же железо. Запущенные
|
||||
разом, они портят числа друг другу, а испорченный оракул хуже отсутствующего:
|
||||
находка выглядит доказанной. Очередь здесь не обосновывается — она правило
|
||||
стадии, и на общее «гони параллельно» не отменяется. Если оператор прямо велел
|
||||
гнать разом **и эту пару** — выполняй, но скажи в границах покрытия, что числа
|
||||
этого прогона сняты под соседней нагрузкой.
|
||||
**Оба помечены «держит машину», поэтому между ними ребро конфликта: они идут
|
||||
цепочкой, а не разом** (правило и его причина — в «Порядок прогона», раздел «Кто
|
||||
держит машину»). Направления у ребра нет: кто первый — неважно. Со стадией 1 они
|
||||
конфликта не имеют и стартуют одновременно с ней; ждать её незачем.
|
||||
|
||||
Цепочка не отменяется общим «гони по графу» — она и есть часть графа. Отменяет
|
||||
её только прямое слово оператора про эту пару, и тогда в границы покрытия идёт
|
||||
строка, что числа прогона сняты под соседней нагрузкой.
|
||||
|
||||
**Эта стадия зарабатывает больше всех остальных вместе, и потому стоит в
|
||||
`standard`, а не только в `deep`.** Измерено на пяти задачах подряд: враждебный
|
||||
@@ -321,6 +380,9 @@ Recall обоих равен длине их источника — это и е
|
||||
|
||||
## Стадия 3 — Independent reimplementation (`deep`, по триггеру)
|
||||
|
||||
Стоит **за барьером стоимости** вместе со стадией 4 — она ради этих двух проходов
|
||||
и существует.
|
||||
|
||||
- `review-reimpl` — пишет свою реализацию, не открывая существующую, затем
|
||||
диффит по решениям. **Запускается по триггеру, а не всегда:** изменение вводит
|
||||
новое правило идентичности, слияния или разбора (проектная формулировка
|
||||
@@ -333,7 +395,11 @@ Recall обоих равен длине их источника — это и е
|
||||
|
||||
## Стадия 4 — Global (`deep`, `design`)
|
||||
|
||||
Агент `review-architecture`. Получает **вход шире диффа**: дерево пакетов с
|
||||
Агент `review-architecture`. В `deep` стоит **за барьером стоимости**, в `design`
|
||||
— в одном ряду с двумя другими проходами. Машину не держит, с `reimpl` конфликта
|
||||
не имеет: за барьером они уходят разом.
|
||||
|
||||
Получает **вход шире диффа**: дерево пакетов с
|
||||
назначением, граф внутренних зависимостей, инвентарь существующих концепций.
|
||||
Команду, которая это готовит, даёт раздел команд `CLAUDE.md`; нет команды —
|
||||
проход собирает карту сам и говорит об этом в границах покрытия.
|
||||
@@ -348,9 +414,14 @@ Recall обоих равен длине их источника — это и е
|
||||
|
||||
## Стадия 5 — Triage (обязательна)
|
||||
|
||||
Агент `review-triage`. Единственный, кто агрегирует. Получает сырые выводы всех
|
||||
проходов, `git diff`, профиль, режим и **список запущенных проходов**; возвращает
|
||||
финальный отчёт.
|
||||
Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.**
|
||||
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
|
||||
стартует. Получает сырые выводы всех проходов, `git diff`, профиль, режим и
|
||||
**список запущенных проходов**; возвращает финальный отчёт.
|
||||
|
||||
Отсюда же правило, которое иначе выглядит придиркой: **триаж на неполном графе не
|
||||
запускается**. Прогон, остановленный барьером или ранним выходом, до стока не
|
||||
доезжает — его отчёт агрегировал бы половину и выглядел бы полным.
|
||||
|
||||
Без триажа проходы дают порядка сорока замечаний при единицах существенных.
|
||||
Потребитель здесь — оркестратор, который **молча реализует** всё, что прочитал:
|
||||
@@ -377,6 +448,30 @@ Recall обоих равен длине их источника — это и е
|
||||
4. вопрос автору дизайна: **«предложи три формы решения и назови компромисс
|
||||
каждой»** — если ответ показывает, что рассматривалась одна, это находка.
|
||||
|
||||
**Граф этого профиля свой, и он плоский.** Гейта нет — кода ещё нет, запускать
|
||||
нечего; машину не держит ни один из трёх; сток — не триаж, а шаг 5 пайплайна
|
||||
задачи, где замечания отрабатываются правкой спек. Триаж здесь не нужен: находок
|
||||
единицы, и каждая либо правит спеку, либо становится развилкой.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
proposal["предложение: proposal.md + дельта-спеки"]
|
||||
specs["specs (режим «дизайн ДО кода»)"]
|
||||
rubric["rubric, фаза 1 → приёмочные критерии в tasks.md"]
|
||||
arch["architecture на предложении"]
|
||||
author["вопрос автору: три формы решения и компромисс каждой"]
|
||||
fix["шаг 5 пайплайна: правка спек, развилки — вопросом в запись"]
|
||||
|
||||
proposal --> specs
|
||||
proposal --> rubric
|
||||
proposal --> arch
|
||||
proposal --> author
|
||||
specs --> fix
|
||||
rubric --> fix
|
||||
arch --> fix
|
||||
author --> fix
|
||||
```
|
||||
|
||||
Смысл профиля: архитектурная находка на готовом коде стоит переписывания и
|
||||
поэтому игнорируется; та же находка на предложении стоит абзаца обсуждения.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user