стоимость ревью: снят проход независимой реализации и самая дорогая модель

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

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

Профиль deep от этого не похудел, а исчез: reimpl был единственным, чем он
отличался от wide, и без него у двух имён оказался бы один состав. Ровно от
этой болезни лечилась ступень wide решением JJJ — у профиля обязан быть один
правильный ответ, иначе реестр состава нечем проверять. Ступеней три: quick,
standard, wide.

Вместе с профилем снято всё, что обслуживало только его. Барьер стоимости —
он держал дорогой проход, чтобы тот не писал реализацию против кода, который
через час перепишут; дорогого прохода нет, граф стал плоским во всех профилях,
рёбер осталось два вида вместо трёх. Тест «идентичность, слияние, разбор» —
полторы страницы, служившие единственной цели: выбрать deep не по ощущению;
вместе с ним ушёл проектный перечень мест в docs/review.md и его скелет в
каноне. Стадии перенумерованы: 0 гейт, 1 сверка, 2 враждебный и
эксплуатационный, 3 архитектурный, 4 триаж — дыра на месте третьей читалась бы
как пропущенная стадия.

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

fable снят с троих: review-triage, review-architecture, doc-code-drift — все на
opus. Основание верхней модели «ошибка распространяется дальше самой находки»
осталось, но оно объясняет, почему двое не опускаются до sonnet, а не почему им
нужна ступень выше opus: разницы в пользу более дорогой модели не показал ни
один прогон, а время и счёт она множила. Палитра схлопнулась до двух цветов,
красного в репозитории больше нет, frontmatter.py теперь отвергнет модель вне
sonnet и opus.

Версия канона не поднята сознательно. Проектам всё равно надо снести перечень
мест для deep из docs/review.md, поэтому пункт вписан в «Что сделать проекту»
записи «Версия 4» — её ещё не гонял ни один проект, оба ждут в TODO.

Тема 33 в DECISIONS.md, следствия 127-129.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
av
2026-08-06 19:02:15 +03:00
co-authored by Claude Opus 5
parent cbfae90f3f
commit ea84a4fbb3
18 changed files with 193 additions and 393 deletions
+70
View File
@@ -2258,3 +2258,73 @@ dev-skills — **маркетплейс плагинов**: скилл комм
126. **Снятое слово называется вместе с заменой и остаётся записанным.** Убрать 126. **Снятое слово называется вместе с заменой и остаётся записанным.** Убрать
из текстов недостаточно: без записи «это снято и вот чем заменено» слово из текстов недостаточно: без записи «это снято и вот чем заменено» слово
возвращается первым же, кто найдёт его удачным. возвращается первым же, кто найдёт его удачным.
## 33. Стоимость ревью: снят самый дорогой проход и самая дорогая модель (2026-08-06)
Прогоны стали долгими, а счёт в токенах — заметным. Разбор шёл не по находкам, а
по статьям расхода: что в конвейере стоит больше всего и что из этого окупается.
Две статьи названы прямо оператором.
**ААББОО. Проход независимой реализации снят целиком, и с ним профиль `deep`.**
`reimpl` писал свою реализацию узла, не открывая существующую, и диффил по
решениям. Его счёт определялся **объёмом вывода** — он один писал код, а не читал
его, — и на прогоне это была самая большая строка расхода. Снят по решению о
стоимости.
Профиль `deep` от этого не «похудел», а исчез: `reimpl` был **единственным**, чем
он отличался от `wide` (обоим оставалось бы 0, 1, 2, 4, 5). Держать два имени для
одного состава нельзя — ровно от этой болезни лечилась ступень `wide` (решение
JJJ): у профиля обязан быть один правильный ответ, иначе реестр состава нечем
проверять. Ступеней теперь три: `quick`, `standard`, `wide`.
Вместе с профилем ушло всё, что обслуживало только его:
- **барьер стоимости** — он существовал ровно затем, чтобы дорогой проход не
писал реализацию против кода, который через час перепишут. Дорогого прохода
нет, и граф стал плоским во всех профилях: от гейта до триажа. Рёбер осталось
два вида вместо трёх — зависимость и конфликт за ресурс;
- **тест «идентичность, слияние, разбор»** (решение из темы 27) — он служил
единственной цели: выбрать `deep` не по ощущению. Выбирать больше нечего, и
полторы страницы теста сняты вместе с проектным перечнем мест в
`docs/review.md`;
- **стадии перенумерованы**: 0 гейт, 1 сверка, 2 враждебный и эксплуатационный,
3 архитектурный, 4 триаж. Дыра на месте третьей читалась бы как пропущенная
стадия.
**ААББПП. Снятие записано как сознательное сужение, а не как «класс оказался
пустым».** `calibration.md` требует замера на двух проектах перед удалением
прохода, и замера не было — было решение о цене. Значит и в «Честном пределе»
стоит честная строка: **«не знаю, чего не знаю» больше не достаёт никто.** Остаток
независимого взгляда дают профиль `design` (код пишется под его находки) и
`architecture` (второй способ, лишние слои), но альтернативной реализации, с
которой можно сдиффить решения, у конвейера нет. Класс уходит в границы покрытия
каждого прогона, а у проекта — в подраздел «перестали проверять сознательно».
Без этой записи снятие через месяц читается как «проверено и признано лишним»,
и вернуть проход было бы не на чем.
**ААББРР. Самая дорогая модель снята со всех проходов.** На ней сидели трое:
`review-triage`, `review-architecture` и `doc-code-drift` из `av-dev-pm`. Все трое
переведены на `opus`. Основание для верхней модели — «ошибка распространяется
дальше самой находки» — никуда не делось, но оно объясняет, почему эти двое **не
опускаются до `sonnet`**, а не почему им нужна ступень выше `opus`: разницы в
пользу более дорогой модели не показал ни один прогон, а время и счёт она множила.
Палитра цветов схлопнулась до двух: `sonnet` → green, `opus` → yellow. Красного в
репозитории больше нет, и `frontmatter.py` теперь отвергнет модель вне этих двух —
раскладка проверяется механически, как и раньше.
### Что из этого следует
127. **Профиль, у которого не осталось собственного прохода, — не профиль.**
Ступень стоимости определяется тем, что она **добавляет**; сняли добавку —
сняли ступень, а не оставили имя. Иначе два имени указывают на один прогон, и
состав снова нечем проверить.
128. **Удаление по цене и удаление по замеру записываются по-разному.** Первое
обязано назвать класс, который перестал проверяться, и оставить его в
границах покрытия. Второе — сослаться на замер. Смешение их даёт самый
дорогой вид тишины: пробел, выглядящий как решённый вопрос.
129. **Механика, обслуживающая один проход, снимается вместе с ним.** Барьер
стоимости, тест выбора верхней ступени и проектный перечень мест держались
только на `reimpl`. Оставшись, они выглядели бы работающими правилами и
тратили бы внимание на каждом прогоне.
+2 -3
View File
@@ -29,9 +29,8 @@
- `task-pipeline` — задача через полный цикл SDD, от постановки до коммита; - `task-pipeline` — задача через полный цикл SDD, от постановки до коммита;
- `task-batch` — несколько задач разом, каждая в своём worktree; - `task-batch` — несколько задач разом, каждая в своём worktree;
- `review-pipeline` — конвейер ревью: гейт, сверка со спеками, враждебные - `review-pipeline` — конвейер ревью: гейт, сверка со спеками, враждебные
постановки, эксплуатационный постмортем, независимая реализация, постановки, эксплуатационный постмортем, архитектура, обязательный триаж.
архитектура, обязательный триаж. Девять агентов-проходов, четыре ступени Восемь агентов-проходов, три ступени стоимости: `quick`, `standard`, `wide`.
стоимости: `quick`, `standard`, `wide`, `deep`.
- **av-dev-git** — `commit`: сообщения в личном стиле. - **av-dev-git** — `commit`: сообщения в личном стиле.
Соглашение об именах: имя **плагина** длинное с префиксом `av-dev-`, имена Соглашение об именах: имя **плагина** длинное с префиксом `av-dev-`, имена
+4
View File
@@ -208,3 +208,7 @@ jellybit 43. Шаги повышения — [changelog.md](av-dev-pm/skills/can
каждого `fix` и `Вопрос` + `Куда ляжет ответ` у каждого `research` пишутся каждого `fix` и `Вопрос` + `Куда ляжет ответ` у каждого `research` пишутся
по мере того, как задача идёт в набор (`sprint take` без них откажет). по мере того, как задача идёт в набор (`sprint take` без них откажет).
Сколько записей готово к взятию, печатает блок здоровья `check` Сколько записей готово к взятию, печатает блок здоровья `check`
- [ ] `docs/review.md`, «Триггеры профиля»: снести перечень мест для `deep`
профиль упразднён вместе с проходом независимой реализации; перечень для
`wide` остаётся. Там же: класс «форма решения, где спека выбора не сделала»
— в подраздел «перестали проверять сознательно»
@@ -2,8 +2,8 @@
name: review-architecture name: review-architecture
description: "Архитектурный проход ревью — получает вход шире диффа (дерево пакетов, граф внутренних зависимостей, инвентарь существующих концепций). Главный вопрос — концептуальная целостность: вводит ли изменение новое понятие, можно ли выразить существующими (включая конструкции стандартной библиотеки), не появился ли второй способ делать то, что уже делается, не размывается ли граница домена. Потолок 3 находки плюс секция «дешевле переделать до мерджа». Работает и на предложении до кода (профиль design). Только чтение." description: "Архитектурный проход ревью — получает вход шире диффа (дерево пакетов, граф внутренних зависимостей, инвентарь существующих концепций). Главный вопрос — концептуальная целостность: вводит ли изменение новое понятие, можно ли выразить существующими (включая конструкции стандартной библиотеки), не появился ли второй способ делать то, что уже делается, не размывается ли граница домена. Потолок 3 находки плюс секция «дешевле переделать до мерджа». Работает и на предложении до кода (профиль design). Только чтение."
tools: Read, Grep, Glob, Bash tools: Read, Grep, Glob, Bash
model: fable model: opus
color: red color: yellow
--- ---
Ты — архитектурный проход ревью. Агент, видящий только дифф, физически не может Ты — архитектурный проход ревью. Агент, видящий только дифф, физически не может
+1 -1
View File
@@ -147,7 +147,7 @@ color: green
- архитектурные границы и второй способ делать то же самое — - архитектурные границы и второй способ делать то же самое —
`review-architecture`; `review-architecture`;
- стиль, дублирование, лишние слои, «я бы написал иначе» — `review-architecture` - стиль, дублирование, лишние слои, «я бы написал иначе» — `review-architecture`
(лишнее и второй способ) и `review-reimpl` (когда прогон идёт профилем `deep`); (лишнее и второй способ);
- соответствие дельта-спекам — `review-specs`. - соответствие дельта-спекам — `review-specs`.
Видишь такое — не выводи находкой; максимум упомяни строкой в границах покрытия, Видишь такое — не выводи находкой; максимум упомяни строкой в границах покрытия,
-164
View File
@@ -1,164 +0,0 @@
---
name: review-reimpl
description: "Самый дорогой и самый ценный generative-проход ревью — получает спеку и контракты соседей, пишет собственную реализацию во временном каталоге, НЕ ОТКРЫВАЯ существующую, и только потом диффит по решениям (декомпозиция, где обрабатываются ошибки, что вынесено в интерфейс, владение данными, протяжка context, модель конкурентности). Единственный проход, который системно достаёт «не знаю, чего не знаю». Запускается только в профиле deep — он и есть верхняя ступень стоимости. Существующий код не меняет."
tools: Read, Grep, Glob, Bash, Write
model: opus
color: yellow
---
Ты — проход **независимой реализации**. Все остальные проходы смотрят на готовое
решение и потому наследуют его рамку: увидев код, невозможно всерьёз спросить «а
нужен ли здесь вообще этот слой». Ты единственный, кто приходит без рамки — ценой
того, что сперва делаешь работу заново.
Находки — по контракту
`${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/finding-contract.md`
(точный путь конвейер передаёт в задании).
## Что берёшь из документов проекта
- **`docs/passport.md`** — граница домена: твоя версия должна лежать по ту же
сторону, что и существующая, иначе весь дифф по решениям окажется спором о
scope.
- **`CLAUDE.md`, инварианты** — то, что твоя реализация обязана соблюсти
(дословность хранения, «сохранили — значит приняли» и подобное).
- **`docs/research/` и `docs/database.md` вместе** — измеренные объёмы и
представление данных: решение, разумное на сотне записей, неразумно на
миллионе (почему именно вместе — project-facts, «Сшивать обязаны проходы»).
- **`docs/conventions/`** — твоя версия должна быть сравнимой по форме.
Карта «что нужно проходу → где лежит» —
`${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/project-facts.md`.
**Деградация поразрядная.** Нет инвариантов в `CLAUDE.md` — пиши версию по спеке
и конвенциям, но `critical` по основанию «нарушен инвариант проекта» не
присваивай: именно инварианты чаще всего объясняют чужое решение. Нет объёмов в
`docs/research/` — не предполагай их. Строка в границы покрытия называет, чего
именно не было. **Риск конкретно этого прохода при таком пробеле максимален:**
твоя версия проще, потому что не знает, чего проект боится.
**Тебя запускают только в верхнем профиле, `deep`, а не всегда.** Он выбирается
ровно тогда, когда вводится или меняется по существу **правило идентичности,
слияния или разбора**; ты — единственное, чем `deep` отличается от соседней
ступени `wide`.
Класс задан тестом, а не списком, и тест не зависит ни от домена, ни от языка.
Правило сюда попадает, когда сходятся три условия: **вариантов несколько** (двое
добросовестных выберут разное, и оба решения защитимы); **спека между ними не
выбирает** — она требует сравнивать, сливать или разбирать, но не называет исход
в пограничном случае; **неверный выбор не падает**, а даёт правдоподобный
результат и молча меняет смысл данных. Отрицательный тест сильнее: то, что
красит гейт или роняет запрос, — не твой класс. Три слова означают три места на
границе, где данные входят или встречаются: чем определяется, что две вещи одна и
та же (состав ключа, нормализация перед сравнением, дедупликация); что получается
при встрече двух представлений одного (победитель конфликта, накопление против
замещения, тай-брейк при равенстве); как внешнее представление становится
внутренним (границы токенов, извлечение полей, неоднозначный вход). Проектный
перечень мест — в `docs/review.md`, если он там записан; он производен от теста,
а не расширяет его.
**Если ты видишь, что тебя позвали не на этот класс** — изменение ничего не
вводит и не меняет по существу, а правило в нём одновариантно, — скажи это первой
строкой отчёта и работай в полглубины: твоя реализация совпадёт с существующей, и
дифф будет о стиле, а не о решениях. Это строка в границы покрытия, а не отказ
работать.
Вне этого случая твой счёт — самый большой в конвейере (он
определяется объёмом вывода: ты пишешь реализацию целиком), а независимый взгляд
в значительной мере уже дал профиль `design` — код писался под его находки. Если
тебя позвали, значит случай тот самый: работай в полную глубину и не экономь на
фазе 1.
## Фаза 1 — своя реализация. Существующую открывать ЗАПРЕЩЕНО
Тебе дают: требования из дельта-спеки, сигнатуры соседей, с которыми узел
договаривается, назначение узла. Описание внешнего мира (формат входа, поведение
источника) читай в `docs/architecture.md` и в `docs/research/` — это описание
мира, а не реализации под ревью.
Конвенции проекта тоже читай: они не подсказывают форму решения, но твоя версия
должна быть сравнимой.
**Категорически нельзя:** открывать файлы реализации под ревью, читать
`git diff`, `git show`, `git log -p` по ним, грепать по именам функций из них.
Читать соседние пакеты **можно и нужно** — тебе нужны их контракты, иначе ты
напишешь несовместимое. Если непонятно, где проходит граница «сосед против
объекта ревью», спроси у оркестратора, а не подглядывай.
Напиши реализацию во временном каталоге проекта (`tmp/reimpl/<узел>/`).
Требования к ней:
- решает задачу целиком, а не набросок: обработка ошибок, отмена `context`,
граничные случаи;
- собирается, если это достижимо за разумное время; несобирающийся черновик тоже
годится, но пометь это;
- пиши так, как писал бы для этого проекта.
Не подглядывай «чтобы свериться» ни на каком этапе фазы 1. Единственное
подглядывание — после того, как твоя версия дописана.
## Фаза 2 — дифф по решениям, а не по строкам
Теперь открой существующую реализацию. Сравнивай **не текст**, а решения:
- **декомпозиция** — сколько функций и типов, где проведены границы, что
оказалось внутри одной сущности у тебя и разнесено у них (или наоборот);
- **где обрабатываются ошибки** — на каком уровне принимается решение, что
оборачивается, что транслируется, что проглочено; в частности, где проходит
граница «вход принят» против «разбор не удался»;
- **что вынесено в интерфейс** — и есть ли у интерфейса больше одной реализации,
кроме мока;
- **владение данными** — кто создаёт, кто мутирует, что копируется; сохраняется
ли содержимое дословно на всём пути от входа до хранилища, или где-то
происходит перекладывание в свою структуру с потерей незнакомых полей;
- **протяжка `context`** — докуда доходит, где теряется, что происходит при
отмене на середине записи;
- **модель конкурентности** — что параллельно, что защищено, кто кого ждёт; что
происходит с двумя операциями над одним ключом.
## Главное правило вывода
**Расхождение не является дефектом, пока не названо последствие.** «Я бы сделал
иначе» — не находка и не выводится вообще. Находка выглядит так: «разбор разнесён
по трём слоям; чтобы добавить второй источник данных, придётся тронуть все три и
два теста — сейчас это N строк, дальше только дороже».
Твоя версия **не эталон**: ты тоже воспроизводишь медиану публичного кода. Там,
где существующее решение объясняется знанием, которого у тебя не было (история
проекта, реальное поведение внешних систем, цена объёма на живом потоке), — это не
находка, а запись в границы покрытия: «разошлись здесь, вероятно, из-за
контекста, которого я не видел».
Отдельно ценно обратное: место, где **их решение лучше твоего**. Выведи это одной
секцией — оно калибрует доверие к остальным твоим находкам.
## Чего этот проход принципиально не может поймать
- Всё, что зависит от истории проекта и внешних систем: почему выбраны именно
такие настройки, какие грабли уже проходили.
- Соответствие требованиям: ты писал по спеке, но сверять реализацию со спекой —
не твоя работа.
- Дефекты рантайма: гонки, поведение под нагрузкой и на реальном объёме.
- Мелкие нарушения записанных конвенций — их ловит линтер, тебе на них дорого
отвлекаться.
## Формат вывода
1. `## Что я написал` — 5–10 строк: форма твоего решения, ключевые развилки.
2. `## Дифф по решениям` — таблица `Решение | У меня | В коде | Последствие`.
3. Находки по контракту — только те, где последствие названо.
4. `## Где их решение лучше`.
5. Обязательный блок:
```
## Coverage of this pass
- проверено: <какой узел переписан, что сравнивалось>
- не проверялось и почему: <что не успел, где не хватило контракта>
- принципиально недоступно этому проходу: история проекта, поведение внешних систем, рантайм
```
## Ограничения
Пиши **только** в `tmp/reimpl/` внутри проекта (не в системный `/tmp`).
Существующий код не редактируй ни строчкой. Не коммить. За собой `tmp/reimpl/` не
убирай — оркестратор может захотеть посмотреть. Реальные данные из `testdata`
наружу не копируй.
+2 -2
View File
@@ -2,8 +2,8 @@
name: review-triage name: review-triage
description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора. Формирует итоговый отчёт с перечнем запущенных проходов и обязательной секцией границ покрытия." description: "Обязательный финальный проход конвейера ревью — единственный, кто агрегирует. Дедуплицирует находки по причине, добывает оракул для critical/major (пишет падающий тест, гоняет разбор на реальных данных, выполняет команду), понижает неподтверждённое до гипотез, отсеивает вкусовщину, ранжирует по ущербу × вероятности и режет до 7 пунктов. Помечает каждую находку «инлайн» или «развилка» для оркестратора. Формирует итоговый отчёт с перечнем запущенных проходов и обязательной секцией границ покрытия."
tools: Read, Grep, Glob, Bash, Write tools: Read, Grep, Glob, Bash, Write
model: fable model: opus
color: red color: yellow
--- ---
Ты — триаж конвейера ревью. Единственный проход, который видит выводы всех Ты — триаж конвейера ревью. Единственный проход, который видит выводы всех
+77 -182
View File
@@ -1,6 +1,6 @@
--- ---
name: review-pipeline name: review-pipeline
description: "Конвейер ревью изменения — детерминированный гейт, сверка с дельта-спеками в обе стороны, враждебные постановки и эксплуатационный постмортем, архитектурный проход, независимая реализация в верхнем профиле и обязательный триаж. Четыре ступени стоимости: quick, standard, wide, deep. Порядок прогона — граф зависимостей, а не очередь: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, независимая реализация стоит за барьером стоимости, триаж — единственный сток. Линейный прогон — по слову оператора или на занятой машине. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода." description: "Конвейер ревью изменения — детерминированный гейт, сверка с дельта-спеками в обе стороны, враждебные постановки и эксплуатационный постмортем, архитектурный проход и обязательный триаж. Три ступени стоимости: quick, standard, wide. Порядок прогона — граф зависимостей, а не очередь: гейт открывает опиниативные проходы, проходы с пометкой «держит машину» идут цепочкой, триаж — единственный сток. Линейный прогон — по слову оператора или на занятой машине. Проектная специфика приходит из документов канона av-dev-pm. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода."
--- ---
# Конвейер ревью # Конвейер ревью
@@ -83,7 +83,7 @@ description: "Конвейер ревью изменения — детерми
- **его блок вопросов** из «Вопросы к проходам» в `docs/review.md`, если он там - **его блок вопросов** из «Вопросы к проходам» в `docs/review.md`, если он там
есть, — **дословно**. Блок адресован проходу поимённо и выведен из промаха есть, — **дословно**. Блок адресован проходу поимённо и выведен из промаха
этого проекта; заставлять девять charter'ов самим ходить за ним значит этого проекта; заставлять восемь charter'ов самим ходить за ним значит
получить, что за ним ходят двое. Проход отвечает на такие вопросы явно, получить, что за ним ходят двое. Проход отвечает на такие вопросы явно,
дополнительно к обязательным; дополнительно к обязательным;
- **контракт находок** — путь к - **контракт находок** — путь к
@@ -106,8 +106,7 @@ description: "Конвейер ревью изменения — детерми
| Модель | Цвет | Проходы | Почему | | Модель | Цвет | Проходы | Почему |
|---|---|---|---| |---|---|---|---|
| `sonnet` | green | gate, code, ops | вход структурный, критерий записан заранее | | `sonnet` | green | gate, code, ops | вход структурный, критерий записан заранее |
| `opus` | yellow | specs, adversary, rubric, reimpl | суждение без опоры на инструмент | | `opus` | yellow | specs, adversary, rubric, architecture, triage | суждение без опоры на инструмент |
| `fable` | red | triage, architecture | ошибка распространяется дальше самой находки |
**Цвет charter'а кодирует модель, а не роль прохода.** Это единственное **Цвет charter'а кодирует модель, а не роль прохода.** Это единственное
назначение цвета: список агентов читается взглядом, и по нему сразу видно, чем назначение цвета: список агентов читается взглядом, и по нему сразу видно, чем
@@ -116,15 +115,16 @@ description: "Конвейер ревью изменения — детерми
здесь и **проверяется механически** — цвет ставится один раз при заведении здесь и **проверяется механически** — цвет ставится один раз при заведении
charter'а, а модель потом двигает калибровка, и разъезжаются они молча. charter'а, а модель потом двигает калибровка, и разъезжаются они молча.
**Самая дорогая модель — только двум проходам, и это калибровка, а не **Моделей две, и верхняя из них — `opus`; выше неё конвейер не платит.** Замер:
осторожность.** Замер: на первом же прогоне конвейера самые ценные находки дали на первом же прогоне самые ценные находки дали `opus`-проходы — сверка спек дала
`opus`-проходы — сверка спек дала 13 находок с оракулами, а проход про 13 находок с оракулами, а проход про идиоматичность (впоследствии упразднённый) —
идиоматичность (впоследствии упразднённый) — три эксперимента против драйвера БД три эксперимента против драйвера БД с воспроизведёнными числами. Разницы в пользу
с воспроизведёнными числами. Разницы в пользу более дорогой модели на модели **дороже** `opus` не обнаружилось ни на одном проходе, а прогон на ней
опиниативных проходах не обнаружилось — значит платить за неё там не за что. стоил заметно дольше и дороже — значит платить за неё не за что.
Двое, у кого она остаётся, отобраны по одному признаку: **их ошибка Двое из пяти держатся на `opus` по признаку, отдельному от суждения: **их ошибка
распространяется дальше собственной находки.** распространяется дальше собственной находки.** Понижать их до `sonnet` вместе с
остальными дешёвыми проходами нельзя.
- `triage` — через него проходит всё, что оркестратор реализует **молча**: - `triage` — через него проходит всё, что оркестратор реализует **молча**:
ложноположительная находка становится кодом, потерянный `critical` — дефектом. ложноположительная находка становится кодом, потерянный `critical` — дефектом.
@@ -133,11 +133,6 @@ charter'а, а модель потом двигает калибровка, и
потолок в 3 находки делает его дешёвым по выходу, а находка на предложении потолок в 3 находки делает его дешёвым по выходу, а находка на предложении
стоит абзаца против переписывания на готовом коде. Дёшево × высокое плечо. стоит абзаца против переписывания на готовом коде. Дёшево × высокое плечо.
`reimpl` намеренно **не** в этом списке, хотя он самый ценный из generative: его
стоимость определяется объёмом вывода (он пишет реализацию целиком), так что
дорогая модель множит самый большой счёт. Ценность же его — в **независимости**
взгляда, а не в мощности модели.
**Самая дешёвая модель не используется ни на одном проходе, и это не экономия **Самая дешёвая модель не используется ни на одном проходе, и это не экономия
наоборот.** Дешёвая модель на опиниативном проходе даёт правдоподобные находки, наоборот.** Дешёвая модель на опиниативном проходе даёт правдоподобные находки,
которые триаж обязан опровергать оракулом, — а это самая дорогая операция которые триаж обязан опровергать оракулом, — а это самая дорогая операция
@@ -146,27 +141,25 @@ charter'а, а модель потом двигает калибровка, и
Дешёвому проходу просто не осталось работы. Дешёвому проходу просто не осталось работы.
Экономия достигается не понижением модели, а **непуском прохода**: `quick` Экономия достигается не понижением модели, а **непуском прохода**: `quick`
четыре прохода, `deep`восемь. Правило выбора профиля и есть главный четыре прохода, `wide` — семь. Правило выбора профиля и есть главный
рычаг стоимости, и ступеней у него четыре именно поэтому. рычаг стоимости, и ступеней у него три именно поэтому.
## Профили ## Профили
| Профиль | Когда | Стадии | Проходов | | Профиль | Когда | Стадии | Проходов |
|---|---|---|---| |---|---|---|---|
| `quick` | багфикс, локальная правка, доки | 0, 1, 5 | 4 | | `quick` | багфикс, локальная правка, доки | 0, 1, 4 | 4 |
| `standard` | **рабочее умолчание**: поведение, миграция схемы, публичный контракт, инвариант | 0, 1, 2, 5 | 6 | | `standard` | **рабочее умолчание**: поведение, миграция схемы, публичный контракт, инвариант | 0, 1, 2, 4 | 6 |
| `wide` | изменение вводит новое понятие или структурную единицу | 0, 1, 2, 4, 5 | 7 | | `wide` | изменение вводит новое понятие или структурную единицу | 0, 1, 2, 3, 4 | 7 |
| `deep` | изменение вводит новое правило идентичности, слияния или разбора | 0, 1, 2, 3, 4, 5 | 8 |
| `design` | **до кода**, на предложении | specs, плюс rubric и architecture по условию `wide` | 13 | | `design` | **до кода**, на предложении | specs, плюс rubric и architecture по условию `wide` | 13 |
**`wide` назван по тому, что он добавляет: вход шире диффа.** Единственное его **`wide` назван по тому, что он добавляет: вход шире диффа.** Единственное его
отличие от `standard` — архитектурный проход, а тот и получает дерево пакетов, отличие от `standard` — архитектурный проход, а тот и получает дерево пакетов,
граф зависимостей и инвентарь понятий вместо одного диффа. Ступень заведена граф зависимостей и инвентарь понятий вместо одного диффа. Это и **верхняя
потому, что прыжок `standard``deep` стоил самого дорогого прохода конвейера, и ступень**: выше неё конвейер не поднимается, и добавить к семи проходам ему
платить эту цену приходилось за одну архитектурную находку: изменений, которые нечего.
трогают публичный контракт, но не вводят нового правила слияния, — большинство.
**Состав сверяется по этой таблице до коммита.** Реестр из трёх-восьми проходов **Состав сверяется по этой таблице до коммита.** Реестр из трёх-семи проходов
проверяется взглядом — и это единственная защита от промаха, который уже проверяется взглядом — и это единственная защита от промаха, который уже
случился: пропуск прохода **не отличим от прохода без находок** (гейт зелёный, случился: пропуск прохода **не отличим от прохода без находок** (гейт зелёный,
спеки сошлись, отчёт выглядит полным), а заметить его мог бы только триаж, спеки сошлись, отчёт выглядит полным), а заметить его мог бы только триаж,
@@ -177,9 +170,7 @@ charter'а, а модель потом двигает калибровка, и
Правило выбора профиля — **по факту изменения, не по ощущению важности**: Правило выбора профиля — **по факту изменения, не по ощущению важности**:
- вводится или меняется по существу правило, определяющее **идентичность, слияние - изменение вводит **новое понятие или структурную единицу**: новый пакет
или разбор** данных (тест — ниже) → `deep`;
- иначе изменение вводит **новое понятие или структурную единицу**: новый пакет
или слой, новая точка входа, второй способ делать то, что уже делается, перенос или слой, новая точка входа, второй способ делать то, что уже делается, перенос
ответственности между узлами → `wide`; ответственности между узлами → `wide`;
- иначе меняется поведение, видимое снаружи, трогается схема, публичный контракт - иначе меняется поведение, видимое снаружи, трогается схема, публичный контракт
@@ -203,74 +194,11 @@ charter'а, а модель потом двигает калибровка, и
изменился, а от того, что появилось новое понятие: добавленное поле в изменился, а от того, что появилось новое понятие: добавленное поле в
существующем ответе — не концепт. существующем ответе — не концепт.
**Верхняя ступень и есть триггер независимой реализации** — раньше он был Что здесь считается новым понятием, проект может уточнить в `docs/review.md`,
условием *внутри* `deep`, и профиль от этого распадался на два разных прогона под разделе настройки конвейера. Это **уточнение**, а не отмена: не записано —
одним именем. Условие никуда не делось, оно просто переехало туда, где выбирается работает список выше. Проект, где изменение контракта и правда архитектурное
профиль: изменение с новым правилом слияния — единственный случай, когда триаж (публичный SDK, чужие потребители), там же поднимает его до `wide` — и это
называл отсутствие `reimpl` дырой покрытия. уточнение, а не возврат прежнего умолчания.
Что здесь считается новым понятием и что — правилом идентичности, проект может
уточнить в `docs/review.md`, разделе настройки конвейера. Это **уточнение**, а не
отмена: не записано — работает список выше. Проект, где изменение контракта и
правда архитектурное (публичный SDK, чужие потребители), там же поднимает его до
`wide` — и это уточнение, а не возврат прежнего умолчания.
### Идентичность, слияние, разбор — тест, а не список
Три слова названы затем, чтобы верхнюю ступень нельзя было выбрать по ощущению.
Читаются они **тестом**, применимым к любому проекту на любом языке; домен, стек
и имена узлов в тест не входят.
Правило принадлежит этому классу, если сходятся **три условия**:
1. **вариантов несколько** — два добросовестных исполнителя выберут разное, и оба
решения защитимы;
2. **спека между ними не выбирает** — она требует, чтобы вещи сравнивались,
сливались или разбирались, но не называет исход в пограничном случае;
3. **неверный выбор не падает** — он даёт правдоподобный результат и меняет смысл
данных молча.
**Отрицательный тест, и он важнее трёх положительных:** если неверная реализация
красит гейт, роняет запрос или ломает тест — это **не** сюда. Такое ловят проходы
дешевле, и платить за него верхней ступенью не за что.
Отсюда же и причина, по которой класс достался самому дорогому проходу:
независимая реализация **выберет другой вариант**, и дифф между двумя вариантами
и есть находка. Там, где вариант один, она совпадёт с существующей — и верхняя
ступень оплатит подтверждение того, что и так известно.
Три слова — это **три места**, где такие правила водятся, и все три стоят на
границе, где данные входят или встречаются:
| Слово | Вопрос, на который правило отвечает | Что в нём выбирается |
|---|---|---|
| **идентичность** | когда две вещи считаются одной и той же | состав ключа и что в него намеренно не входит; нормализация перед сравнением — регистр, пробелы, кодировка, время, единицы, округление; дедупликация |
| **слияние** | что получается, когда два представления одного встретились | кто побеждает при конфликте; накопительное против замещающего; что делать с отсутствующим полем; тай-брейк при равенстве |
| **разбор** | как внешнее представление становится внутренним | границы токенов; извлечение полей; сопоставление с известным набором; поведение на неоднозначном входе |
**Триггер — новое или изменённое правило, а не код рядом с ним.** Правка
сообщения об ошибке в узле, который разбирает вход, ступень не поднимает.
Поднимают: заводится ключ или меняется его состав; в слияние добавляется источник
или меняется победитель при конфликте; у разбора появляется новый вид входа или
новая ветка неоднозначности. Без этой оговорки проект, чей домен и **состоит** из
таких правил, оказывался бы в `deep` всегда — та же болезнь, от которой лечилась
ступень `wide`.
**Ступень определяет класс правила, а не вид работы.** Миграция схемы сама по
себе `standard` — но миграция, которая **переносит данные** по правилу («сложить
дубли», «привести к одному виду перед сравнением»), несёт правило идентичности и
потому `deep`. Одно и то же слово в описании задачи попадает в разные ступени, и
это не противоречие: смотрят не на слово, а на то, есть ли выбор, которого спека
не сделала.
**Проект, у которого таких правил нет вовсе, `deep` не запускает никогда.** Это
законное состояние, а не признак недонастройки: бывают проекты, где данные
приходят уже нормализованными, ничего ни с чем не сливается, а внешних форматов
нет. Верхняя ступень там просто не срабатывает, и придумывать ей повод не надо.
Свои места проект перечисляет в `docs/review.md`, подраздел «Триггеры профиля» —
поимённо, узлами или capability. Перечень **производен от теста**: он не расширяет
класс, а называет, где этот класс живёт именно здесь.
### Профиль — максимум по поверхности, и отсюда размер задачи ### Профиль — максимум по поверхности, и отсюда размер задачи
@@ -298,18 +226,17 @@ charter'а, а модель потом двигает калибровка, и
Профиль отвечает «какие проходы», порядок — «что кого ждёт». Стадии остаются Профиль отвечает «какие проходы», порядок — «что кого ждёт». Стадии остаются
единицей **состава** (профиль набирается стадиями, см. таблицу выше), но порядок единицей **состава** (профиль набирается стадиями, см. таблицу выше), но порядок
задают **не их номера**: между стадиями 14 настоящих зависимостей нет — ни один задают **не их номера**: между стадиями 13 настоящих зависимостей нет — ни один
проход не читает вывод другого, — и очередь между ними была бы платой ни за что. проход не читает вывод другого, — и очередь между ними была бы платой ни за что.
Рёбер три вида, и они разной природы. Путать их нельзя: первое про Рёбер два вида, и они разной природы. Путать их нельзя: первое про
**осмысленность** (на красном гейте опиниативный проход не о чем), второе про **осмысленность** (на красном гейте опиниативный проход не о чем), второе про
**железо**, третье про **деньги**. **железо**.
| Ребро | Смысл | Между кем | | Ребро | Смысл | Между кем |
|---|---|---| |---|---|---|
| **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | гейт → все опиниативные; все проходы → триаж | | **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | гейт → все опиниативные; все проходы → триаж |
| **конфликт за ресурс** | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» | | **конфликт за ресурс** | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» |
| **барьер стоимости** | дорогое не запускается, пока дешёвое не сказало, что форма изменения выживет | только `deep` |
```mermaid ```mermaid
flowchart TD flowchart TD
@@ -318,24 +245,19 @@ flowchart TD
code["code"] code["code"]
adversary["adversary<br/>(держит машину)"] adversary["adversary<br/>(держит машину)"]
ops["ops<br/>(держит машину)"] ops["ops<br/>(держит машину)"]
architecture["architecture<br/>(wide, deep)"] architecture["architecture<br/>(wide)"]
barrier{{"форма изменения выживает?"}}
reimpl["reimpl"]
triage["triage — единственный сток"] triage["triage — единственный сток"]
gate -->|зелёный| specs gate -->|зелёный| specs
gate -->|зелёный| code gate -->|зелёный| code
gate -->|зелёный| adversary gate -->|зелёный| adversary
gate -->|зелёный| ops gate -->|зелёный| ops
gate -->|"зелёный, wide и deep"| architecture gate -->|"зелёный, wide"| architecture
adversary -. один ресурс — машина .- ops adversary -. один ресурс — машина .- ops
specs --> barrier specs --> triage
code --> barrier code --> triage
adversary --> barrier adversary --> triage
ops --> barrier ops --> triage
barrier -->|"deep"| reimpl
barrier -->|"quick, standard, wide: барьера нет"| triage
reimpl --> triage
architecture --> triage architecture --> triage
``` ```
@@ -370,7 +292,7 @@ flowchart TD
| `adversary` | да | находка есть **построенный путь**: он пишет падающий тест и гоняет его | | `adversary` | да | находка есть **построенный путь**: он пишет падающий тест и гоняет его |
| `ops` | да | доказывает числами: время удержания блокировки, пик кучи, темп роста журнала | | `ops` | да | доказывает числами: время удержания блокировки, пик кучи, темп роста журнала |
| `triage` | да | проверяет оракул `critical`/`major` запуском — но он сток и тоже один | | `triage` | да | проверяет оракул `critical`/`major` запуском — но он сток и тоже один |
| `specs`, `code`, `reimpl`, `architecture`, `rubric` | нет | читают и рассуждают; `reimpl` пишет свою реализацию в черновик, но не исполняет её | | `specs`, `code`, `architecture`, `rubric` | нет | читают и рассуждают, ничего не исполняют |
**Правило про ресурс, а не про имена.** Раньше здесь стояло именованное **Правило про ресурс, а не про имена.** Раньше здесь стояло именованное
исключение «`adversary` и `ops`»; оно рассыпается, как только проход начнёт исключение «`adversary` и `ops`»; оно рассыпается, как только проход начнёт
@@ -385,39 +307,26 @@ flowchart TD
Проект вправе пометить «держит машину» и другой проход — в `docs/review.md`, Проект вправе пометить «держит машину» и другой проход — в `docs/review.md`,
разделе настройки конвейера. Снимать пометку с перечисленных нельзя. разделе настройки конвейера. Снимать пометку с перечисленных нельзя.
### Барьер стоимости — вместо раннего выхода ### Находка «переделать форму» — прогон повторяется целиком
Барьер существует ровно там, где ранний выход зарабатывал: `reimpl` пишет **Барьера стоимости в конвейере нет, и раннего выхода тоже.** Барьер существовал
реализацию целиком и потому самый дорогой проход конвейера. Если дешёвая часть ради независимой реализации — единственного прохода, чей счёт определялся объёмом
нашла, что **форму изменения** надо переделывать, он будет писать её против кода, вывода, — и ушёл вместе с ней. Граф во всех профилях плоский, от гейта до триажа:
которого через час не станет. защищать за барьером нечего, `architecture` дёшев по выходу (потолок 3 находки), а
сериализация не бесплатна — она разводит по очереди то, что могло идти разом.
- **прошло без находок «переделать форму»** — барьер открыт, `reimpl` уходит; Находка «**форму изменения** надо переделывать» ловится триажем, как и любая
- **есть такая находка** — прогон останавливается, находка чинится, конвейер другая; дальше правило одно. Находка чинится, и конвейер запускается **заново с
запускается **заново с нулевой стадии**, а не «доезжает» остатком по старому нулевой стадии**, а не «доезжает» остатком по коду, которого через час не станет.
коду. Незапущенные проходы идут в границы покрытия строкой «не запускался: Если прогон всё же остановлен на полпути, незапущенные проходы идут в границы
прогон остановлен на <проход> из-за <находка>», поимённо. Триаж на половине покрытия строкой «не запускался: прогон остановлен на <проход> из-за <находка>»,
прогона не запускается: его отчёт выглядит полным, потому что агрегирует всё, поимённо, а **триаж на половине прогона не запускается**: его отчёт выглядит
что ему подали, — это тот же молчащий пропуск, что и в разделе «Профили»; полным, потому что агрегирует всё, что ему подали, — это тот же молчащий пропуск,
- **находка чинится в пределах существующей формы** (`Действие: инлайн`) — что и в разделе «Профили».
барьер не срабатывает: дешевле дособрать все находки и починить пачкой, чем
гонять конвейер дважды.
**`architecture` стоит за барьером только там, где барьер и так есть.** В `deep` Находка, которая чинится в пределах существующей формы (`Действие: инлайн`),
он уходит вместе с `reimpl` — ждать ему всё равно нечего. В `wide` он стартует прогон не останавливает: дешевле дособрать все находки и починить пачкой, чем
сразу после зелёного гейта, в одном ряду со стадиями 1 и 2: своего барьера он не гонять конвейер дважды.
заслуживает. Потолок в 3 находки делает его дешёвым, а барьер не бесплатен — он
сериализует то, что могло идти разом, и платить сериализацией за один дешёвый
проход не за что. Есть и вторая причина, помельче: барьер спрашивает «выживает ли
форма изменения», а `architecture` — как раз тот, кто на этот вопрос отвечает.
В `quick`, `standard` и `wide` барьера нет — за ним нечего защищать: стадии 3 в
этих профилях не бывает, и граф там плоский от гейта до триажа. Находка «переделать
форму» ловится в них триажем, а прогон после починки повторяется целиком: платить
за это нечем, дорогих проходов в этих профилях нет. В `design` его тоже
нет, и по другой причине: там предметом и является форма, а все три прохода
читают одно предложение — защищать нечего, у графа этого профиля своя форма (см.
его раздел).
### Линеаризация — когда графа мало ### Линеаризация — когда графа мало
@@ -478,7 +387,7 @@ flowchart TD
Recall обоих равен длине их источника — это и есть предел applicative-проходов, Recall обоих равен длине их источника — это и есть предел applicative-проходов,
ради которого существует стадия 2. ради которого существует стадия 2.
## Стадия 2 — Adversarial и operational (`standard`, `wide`, `deep`) ## Стадия 2 — Adversarial и operational (`standard`, `wide`)
Два прохода: Два прохода:
@@ -495,7 +404,7 @@ Recall обоих равен длине их источника — это и е
строка, что числа прогона сняты под соседней нагрузкой. строка, что числа прогона сняты под соседней нагрузкой.
**Эта стадия зарабатывает больше всех остальных вместе, и потому стоит уже в **Эта стадия зарабатывает больше всех остальных вместе, и потому стоит уже в
`standard`, а не только в верхних профилях.** Измерено на пяти задачах подряд: враждебный `standard`, а не только в верхнем профиле.** Измерено на пяти задачах подряд: враждебный
проход дал пять из семи выживших находок дозапуска (включая обе верхние); проход дал пять из семи выживших находок дозапуска (включая обе верхние);
эксплуатационный — единственный, кто нашёл, что откат бинаря поверх новой схемы эксплуатационный — единственный, кто нашёл, что откат бинаря поверх новой схемы
стартует молча. Оба несут внешний оракул по построению: один обязан путь стартует молча. Оба несут внешний оракул по построению: один обязан путь
@@ -508,38 +417,10 @@ Recall обоих равен длине их источника — это и е
раздел «Сшивать обязаны проходы». Без этих документов стадия вырождается в общие раздел «Сшивать обязаны проходы». Без этих документов стадия вырождается в общие
места. места.
## Стадия 3 — Independent reimplementation (только `deep`) ## Стадия 3 — Global (`wide`, `design`)
Единственный проход, ради которого существует **барьер стоимости**, и Агент `review-architecture`. Уходит в первой волне, сразу после зелёного гейта, в
единственное, что отличает `deep` от `wide`. одном ряду со стадиями 1 и 2. Машину не держит, ждать ему нечего.
- `review-reimpl` — пишет свою реализацию, не открывая существующую, затем
диффит по решениям. **Профиль и есть его условие:** `deep` выбирается ровно
тогда, когда вводится или меняется по существу правило идентичности, слияния
или разбора — по тесту из раздела «Идентичность, слияние, разбор»; проектный
перечень мест, где такие правила живут, — в `docs/review.md`, если записан. Это
самый дорогой проход конвейера (его счёт определяется объёмом вывода — он пишет
реализацию целиком), а вне этого случая независимый взгляд в значительной мере
уже дал профиль `design`: код писался под его находки. Условие выбрано по факту:
единственный раз, когда триаж назвал отсутствие `reimpl` дырой покрытия, — это
была задача с новым правилом слияния сущностей.
Такие правила обычно занимают десятки строк, но определяют смысл **всех** данных
проекта. Отсюда особенность верхней ступени, из-за которой её легко выбрать
неверно: самый дорогой проход тратится на самый **маленький** дифф. `deep` не про
размер изменения и не про его опасность — он про класс правила.
Раньше это условие стояло **внутри** профиля, и `deep` означал то семь проходов,
то восемь. Реестр состава, который «проверяется взглядом», проверять было нечем:
у профиля не было одного правильного ответа. Теперь ступеней две — `wide` и
`deep`, — и у каждой состав ровно один.
## Стадия 4 — Global (`wide`, `deep`, `design`)
Агент `review-architecture`. В `deep` стоит **за барьером стоимости** (ждать ему
там всё равно нечего), в `wide` и `design` — в первой волне, сразу после старта
профиля. Машину не держит, с `reimpl` конфликта не имеет: за барьером они уходят
разом.
**Условие этой стадии и есть условие ступени `wide`:** изменение вводит новое **Условие этой стадии и есть условие ступени `wide`:** изменение вводит новое
понятие или структурную единицу. Не «изменение крупное» и не «изменение опасное»: понятие или структурную единицу. Не «изменение крупное» и не «изменение опасное»:
@@ -562,7 +443,7 @@ Recall обоих равен длине их источника — это и е
конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс
секция «дешевле переделать до мерджа». секция «дешевле переделать до мерджа».
## Стадия 5 — Triage (обязательна) ## Стадия 4 — Triage (обязательна)
Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.** Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.**
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
@@ -570,8 +451,8 @@ Recall обоих равен длине их источника — это и е
**список запущенных проходов**; возвращает финальный отчёт. **список запущенных проходов**; возвращает финальный отчёт.
Отсюда же правило, которое иначе выглядит придиркой: **триаж на неполном графе не Отсюда же правило, которое иначе выглядит придиркой: **триаж на неполном графе не
запускается**. Прогон, остановленный барьером или ранним выходом, до стока не запускается**. Прогон, остановленный на полпути находкой «переделать форму», до
доезжает — его отчёт агрегировал бы половину и выглядел бы полным. стока не доезжает — его отчёт агрегировал бы половину и выглядел бы полным.
Без триажа проходы дают порядка сорока замечаний при единицах существенных. Без триажа проходы дают порядка сорока замечаний при единицах существенных.
Потребитель здесь — оркестратор, который **молча реализует** всё, что прочитал: Потребитель здесь — оркестратор, который **молча реализует** всё, что прочитал:
@@ -608,7 +489,7 @@ Recall обоих равен длине их источника — это и е
мелкой нарезке это самая большая статья конвейера. Рубрика же на узел, который не мелкой нарезке это самая большая статья конвейера. Рубрика же на узел, который не
вводит нового понятия, порождает свойства уже существующего рода — те, что и так вводит нового понятия, порождает свойства уже существующего рода — те, что и так
записаны конвенциями и спеками; а `architecture` без нового понятия отвечает «нет» записаны конвенциями и спеками; а `architecture` без нового понятия отвечает «нет»
на свой главный вопрос ещё до запуска (см. «Стадия 4»). на свой главный вопрос ещё до запуска (см. «Стадия 3»).
**Граф этого профиля свой, и он плоский.** Гейта нет — кода ещё нет, запускать **Граф этого профиля свой, и он плоский.** Гейта нет — кода ещё нет, запускать
нечего; машину не держит ни один проход; сток — не триаж, а шаг 5 пайплайна нечего; машину не держит ни один проход; сток — не триаж, а шаг 5 пайплайна
@@ -712,6 +593,20 @@ flowchart TD
никто. Класс обратимый — портит форму кода, не данные, — и его надо признавать в никто. Класс обратимый — портит форму кода, не данные, — и его надо признавать в
границах покрытия, а не считать проверенным. границах покрытия, а не считать проверенным.
Так же честно и про второй упразднённый проход: **«не знаю, чего не знаю» больше
не достаёт никто.** Проход независимой реализации писал свою версию узла, не
открывая существующую, и диффил по решениям — декомпозиция, владение данными,
модель конкурентности, форма решения там, где спека выбора не сделала. Он снят по
решению оператора о **стоимости** — счёт определялся объёмом вывода, и на прогон
он тратил больше всех остальных проходов вместе, — а не по замеру, который
[calibration.md](references/calibration.md) требует перед удалением. Значит и
записывается это как сознательное сужение, а не как «класс оказался пустым»:
остаток независимого взгляда даёт профиль `design` (код пишется под его находки) и
`architecture` (второй способ, лишние слои), но **альтернативной реализации, с
которой можно сдиффить решения, у конвейера теперь нет**. Класс идёт строкой в
границы покрытия каждого прогона — там же, где проект перечисляет своё в
подразделе «перестали проверять сознательно».
Это и есть причина, по которой конвейер готовит ревью, а не заменяет его. Это и есть причина, по которой конвейер готовит ревью, а не заменяет его.
## Ссылки ## Ссылки
@@ -83,7 +83,6 @@ stateDiagram-v2
| `review-specs` | поведение вне спеки | добавить незаказанный фолбэк-дефолт на пустом входе | | `review-specs` | поведение вне спеки | добавить незаказанный фолбэк-дефолт на пустом входе |
| `review-code` | нарушение прозаической конвенции | увести штатный отказ мимо единой точки трансляции ошибки | | `review-code` | нарушение прозаической конвенции | увести штатный отказ мимо единой точки трансляции ошибки |
| `review-rubric` | нарушенное свойство узла | у клиента внешнего сервиса убрать таймаут и протяжку `context` | | `review-rubric` | нарушенное свойство узла | у клиента внешнего сервиса убрать таймаут и протяжку `context` |
| `review-reimpl` | форма решения | размазать решение по трём слоям там, где хватало одной функции |
| `review-architecture` | второй способ | завести вторую точку генерации id мимо единой | | `review-architecture` | второй способ | завести вторую точку генерации id мимо единой |
| `review-adversary` | построенный путь | принять внешний идентификатор без разбора до запроса в хранилище | | `review-adversary` | построенный путь | принять внешний идентификатор без разбора до запроса в хранилище |
| `review-ops` | деградация окружения | убрать обработку недоступности внешней зависимости в фоновом цикле | | `review-ops` | деградация окружения | убрать обработку недоступности внешней зависимости в фоновом цикле |
@@ -38,8 +38,8 @@
- **`critical` по основанию «нарушен инвариант проекта» требует инвариантов.** - **`critical` по основанию «нарушен инвариант проекта» требует инвариантов.**
Ссылка идёт на пункт раздела инвариантов `CLAUDE.md` дословно. Без них основание Ссылка идёт на пункт раздела инвариантов `CLAUDE.md` дословно. Без них основание
недоступно — см. [project-facts.md](project-facts.md), поразрядная деградация. недоступно — см. [project-facts.md](project-facts.md), поразрядная деградация.
- **Расхождение — не дефект, пока не названо последствие.** Особенно для прохода - **Расхождение — не дефект, пока не названо последствие.** Особенно для
независимой реализации: «я бы сделал иначе» без последствия не выводится. архитектурного прохода: «я бы сделал иначе» без последствия не выводится.
## Шкала severity ## Шкала severity
@@ -42,7 +42,7 @@
«запись лежит сжатой и распаковывается целиком»; «блокировка удерживалась «запись лежит сжатой и распаковывается целиком»; «блокировка удерживалась
5.019 с» — гарантированный отказ соседа только рядом с известным таймаутом 5.019 с» — гарантированный отказ соседа только рядом с известным таймаутом
занятости. Числа в `docs/research/`, настройки в `docs/database.md`, и оба занятости. Числа в `docs/research/`, настройки в `docs/database.md`, и оба
читает `ops`, `adversary`, `reimpl`. читает `ops` и `adversary`.
- **инвариант + обратимость.** severity берётся из `CLAUDE.md`; если её там - **инвариант + обратимость.** severity берётся из `CLAUDE.md`; если её там
нет — она **выводится по обратимости последствия** и помечается «выведена по нет — она **выводится по обратимости последствия** и помечается «выведена по
обратимости», а не выдаётся за решение проекта. обратимости», а не выдаётся за решение проекта.
@@ -63,7 +63,7 @@
| --- | --- | | --- | --- |
| `CLAUDE.md` без инвариантов | `critical` по основанию «нарушен инвариант проекта» не присваивается никем | | `CLAUDE.md` без инвариантов | `critical` по основанию «нарушен инвариант проекта» не присваивается никем |
| `docs/security.md` | `adversary` не знает периметра — формулирует условиями, `critical` не ставит | | `docs/security.md` | `adversary` не знает периметра — формулирует условиями, `critical` не ставит |
| `docs/research/` | числа неизвестны `specs`, `ops`, `adversary`, `reimpl` — формулируют условиями, а `specs` теряет проверку «требование против наблюдения» | | `docs/research/` | числа неизвестны `specs`, `ops`, `adversary` — формулируют условиями, а `specs` теряет проверку «требование против наблюдения» |
| `docs/database.md` | замер не с чем сравнить: находка не поднимается выше гипотезы | | `docs/database.md` | замер не с чем сравнить: находка не поднимается выше гипотезы |
| `docs/passport.md` | `architecture` теряет границу домена и вырождается в общее мнение | | `docs/passport.md` | `architecture` теряет границу домена и вырождается в общее мнение |
| `docs/review.md` | `triage` отсеивает вслепую: типовых ложноположительных нет | | `docs/review.md` | `triage` отсеивает вслепую: типовых ложноположительных нет |
+3 -3
View File
@@ -115,7 +115,7 @@ description: Проводит несколько задач разом — пл
где уже мерили или уже ломалось. где уже мерили или уже ломалось.
Ни один триггер не сработал — задача не замеряющая, даже если её ревью Ни один триггер не сработал — задача не замеряющая, даже если её ревью
окажется `deep`. Профиль про глубину проверки, замеряющая — про соревнование за окажется `wide`. Профиль про глубину проверки, замеряющая — про соревнование за
железо; это разные вопросы, и совпадают они не всегда; железо; это разные вопросы, и совпадают они не всегда;
- **нумерованные артефакты — номера раздаёт оркестратор заранее.** Если проект - **нумерованные артефакты — номера раздаёт оркестратор заранее.** Если проект
нумерует миграции (путь — `docs/.pm.json`, ключ `migrations`), посмотри последний нумерует миграции (путь — `docs/.pm.json`, ключ `migrations`), посмотри последний
@@ -241,8 +241,8 @@ flowchart TD
а не сабагент: батч идёт по одной задаче → режим умолчательный, **`по а не сабагент: батч идёт по одной задаче → режим умолчательный, **`по
графу`** (машина свободна); батч идёт волнами → **`линейно`**, твой worktree графу`** (машина свободна); батч идёт волнами → **`линейно`**, твой worktree
не один на машине, и этой причиной ты обязан объяснить режим в отчёте. не один на машине, и этой причиной ты обязан объяснить режим в отчёте.
Внутренние рёбра графа — цепочку проходов, держащих машину, и барьер Внутренние рёбра графа — цепочку проходов, держащих машину — конвейер
стоимости — конвейер соблюдает сам, в любом режиме; соблюдает сам, в любом режиме;
- **если вложенные сабагенты недоступны** (движок не даёт запускать агентов из - **если вложенные сабагенты недоступны** (движок не даёт запускать агентов из
агента) — не пропускай ревью и не понижай профиль: проведи его **инлайн** по агента) — не пропускай ревью и не понижай профиль: проведи его **инлайн** по
тем же charter'ам `av-dev-pipeline`, сохранив обязательное — гейт до тем же charter'ам `av-dev-pipeline`, сохранив обязательное — гейт до
@@ -270,8 +270,7 @@ flowchart TD
**Режим по умолчанию — `по графу`, и обосновывать его не надо.** Конвейер сам **Режим по умолчанию — `по графу`, и обосновывать его не надо.** Конвейер сам
знает свои рёбра: гейт открывает опиниативные проходы, проходы с пометкой «держит знает свои рёбра: гейт открывает опиниативные проходы, проходы с пометкой «держит
машину» идут цепочкой (иначе замеры портят друг друга и находка выглядит машину» идут цепочкой (иначе замеры портят друг друга и находка выглядит
доказанной), дорогие generative-проходы ждут барьера стоимости, триаж — сток. доказанной), триаж — сток. Твоего участия это не требует.
Твоего участия это не требует.
Просить **`линейно`** нужно только по причине, и она называется строкой: так Просить **`линейно`** нужно только по причине, и она называется строкой: так
сказал оператор; машина занята чем-то ещё (в том числе соседней задачей батча); сказал оператор; машина занята чем-то ещё (в том числе соседней задачей батча);
+2 -2
View File
@@ -2,8 +2,8 @@
name: doc-code-drift name: doc-code-drift
description: "Сверка документов канона с кодом по закрытому перечню проверяемых фактов: имя основной ветки и команды из CLAUDE.md, запреты с путями, testdata и временный каталог, путь миграций из .pm.json, внешние зависимости поимённо в architecture.md против манифеста, настройки с числовым значением в database.md против конфига и кода, единые точки проекта против реального числа реализаций, capability против существующих модулей. Отвечает на «этот факт ещё верен», а не «эта архитектура правильная». Читает весь репозиторий, гоняет только читающие команды. Отдаёт готовые формулировки и ничего не правит сам. Согласованность документов между собой смотрит агент doc-consistency. Использовать на сессии между спринтами, а также после приведения проекта к канону (adopt) и после повышения версии канона (upgrade). Только чтение." description: "Сверка документов канона с кодом по закрытому перечню проверяемых фактов: имя основной ветки и команды из CLAUDE.md, запреты с путями, testdata и временный каталог, путь миграций из .pm.json, внешние зависимости поимённо в architecture.md против манифеста, настройки с числовым значением в database.md против конфига и кода, единые точки проекта против реального числа реализаций, capability против существующих модулей. Отвечает на «этот факт ещё верен», а не «эта архитектура правильная». Читает весь репозиторий, гоняет только читающие команды. Отдаёт готовые формулировки и ничего не правит сам. Согласованность документов между собой смотрит агент doc-consistency. Использовать на сессии между спринтами, а также после приведения проекта к канону (adopt) и после повышения версии канона (upgrade). Только чтение."
tools: Read, Grep, Glob, Bash tools: Read, Grep, Glob, Bash
model: fable model: opus
color: red color: yellow
--- ---
Ты — **сверка документов канона с кодом**. Один вопрос: **этот факт ещё верен?** Ты — **сверка документов канона с кодом**. Один вопрос: **этот факт ещё верен?**
+8 -9
View File
@@ -106,12 +106,12 @@ kebab-case.** Причина не эстетическая: имя файла с
| Документ | Вопрос | Кто читает, кроме человека | | Документ | Вопрос | Кто читает, кроме человека |
| --- | --- | --- | | --- | --- | --- |
| `CLAUDE.md` | что нельзя нарушать, чем краснеет гейт | все агенты, всегда | | `CLAUDE.md` | что нельзя нарушать, чем краснеет гейт | все агенты, всегда |
| `passport.md` | зачем и для кого, чем это **не** является | `architecture`, `rubric`, `reimpl`, `specs` | | `passport.md` | зачем и для кого, чем это **не** является | `architecture`, `rubric`, `specs` |
| `architecture.md` | как сложено и где что работает | все проходы ревью | | `architecture.md` | как сложено и где что работает | все проходы ревью |
| `database.md` | что лежит в хранилище и какими настройками | `ops`, `adversary`, `reimpl` | | `database.md` | что лежит в хранилище и какими настройками | `ops`, `adversary` |
| `security.md` | против кого защищаемся и что вне модели | `adversary` | | `security.md` | против кого защищаемся и что вне модели | `adversary` |
| `conventions/` | как мы пишем код | `code` | | `conventions/` | как мы пишем код | `code` |
| `research/` | что показала реальность, а не документация | `specs`, `reimpl`, `ops`, `adversary` | | `research/` | что показала реальность, а не документация | `specs`, `ops`, `adversary` |
| `adr/` | почему решено именно так | `architecture` | | `adr/` | почему решено именно так | `architecture` |
| `review.md` | как настроен конвейер и что уже проскакивало | `triage`, каждый проход — свою часть | | `review.md` | как настроен конвейер и что уже проскакивало | `triage`, каждый проход — свою часть |
| `openspec/specs/` | что система делает — нормативно | `specs` | | `openspec/specs/` | что система делает — нормативно | `specs` |
@@ -232,12 +232,11 @@ kebab-case.** Причина не эстетическая: имя файла с
- **Вопросы к проходам** — поимённо, в форме `<имя прохода>: <вопрос> - **Вопросы к проходам** — поимённо, в форме `<имя прохода>: <вопрос>
(<провенанс>)`; (<провенанс>)`;
- **Триггеры профиля** — проектная конкретизация правила выбора профиля ревью: - **Триггеры профиля** — проектная конкретизация правила выбора профиля ревью:
что в этом проекте считается **новым понятием или структурной единицей** (это что в этом проекте считается **новым понятием или структурной единицей** это
поднимает прогон до `wide`) и **где живут правила идентичности, слияния и поднимает прогон до `wide`, верхней ступени. Перечнем мест, а не вторым
разбора** (до `deep`) — перечнем мест, производным от теста конвейера, а не определением класса. Уточняет умолчания, а не отменяет их. Рабочее умолчание —
вторым определением класса. Уточняет умолчания, а не отменяет их. Рабочее `standard`: миграция схемы и публичный контракт ступень **не** поднимают, их
умолчание `standard`: миграция схемы и публичный контракт ступень **не** проверяют проходы, которые в `standard` и так есть;
поднимают, их проверяют проходы, которые в `standard` и так есть;
- **Недоступно проверке** — два подраздела: «не проверит ни один проход» - **Недоступно проверке** — два подраздела: «не проверит ни один проход»
(принципиальная граница, по факту промаха не пересматривается) и «перестали (принципиальная граница, по факту промаха не пересматривается) и «перестали
проверять сознательно» (пересматривается первым). проверять сознательно» (пересматривается первым).
+14 -7
View File
@@ -127,13 +127,20 @@ upgrade` идёт по записям снизу вверх от версии п
человека. **Переименование ADR это перенос ссылок**: слаг стоит в человека. **Переименование ADR это перенос ссылок**: слаг стоит в
`adr/README.md`, в `architecture.md` и в чужих документах, и делается одним `adr/README.md`, в `architecture.md` и в чужих документах, и делается одним
проходом, иначе останутся битые ссылки (их `docs.py` потом и покажет). проходом, иначе останутся битые ссылки (их `docs.py` потом и покажет).
8. `docs/.pm.json`: `"canon": 4`. 8. `docs/review.md`, подраздел «Триггеры профиля»: снести перечень мест для
9. Позвать **обоих судей**`doc-consistency` и `doc-code-drift`, шагом 6 `deep` — профиль упразднён вместе с проходом независимой реализации, и
`upgrade`. Пунктов выше девять, половина из них ручная, и именно здесь видно, перечень стал указателем в пустоту. Перечень для `wide` (новое понятие или
какие сделаны только наполовину: переименования секций и полей разводят структурная единица) остаётся: он и поднимает прогон до верхней ступени. Там
документы, а `check` сверяет число версии, а не существо. Первый прогон на же проверить журнал дефектов и «Недоступно проверке» на упоминания
живом проекте вдобавок самый урожайный — правило единственного дома до сих пор независимой реализации: класс «форма решения, где спека выбора не сделала»
никто не проверял. Разбирать порциями, а не одним заходом. переезжает в подраздел «перестали проверять сознательно».
9. `docs/.pm.json`: `"canon": 4`.
10. Позвать **обоих судей**`doc-consistency` и `doc-code-drift`, шагом 6
`upgrade`. Пунктов выше десять, половина из них ручная, и именно здесь видно,
какие сделаны только наполовину: переименования секций и полей разводят
документы, а `check` сверяет число версии, а не существо. Первый прогон на
живом проекте вдобавок самый урожайный — правило единственного дома до сих пор
никто не проверял. Разбирать порциями, а не одним заходом.
## Версия 3 — 2026-08-04 ## Версия 3 — 2026-08-04
+2 -10
View File
@@ -284,19 +284,11 @@
### Триггеры профиля ### Триггеры профиля
Проектная конкретизация правила выбора профиля: что здесь считается **новым Проектная конкретизация правила выбора профиля: что здесь считается **новым
понятием или структурной единицей** (поднимает прогон до `wide` и запускает понятием или структурной единицей** — это поднимает прогон до `wide`, верхней
архитектурный проход) и **где живут правила идентичности, слияния и разбора** ступени, и запускает архитектурный проход. Перечнем узлов или capability,
(до `deep`, запускает независимую реализацию) — перечнем узлов или capability,
поимённо. Уточняет умолчания конвейера, не отменяет их; рабочее умолчание — поимённо. Уточняет умолчания конвейера, не отменяет их; рабочее умолчание —
`standard`. `standard`.
Перечень для `deep` **производен от теста конвейера**, а не заменяет его:
правило попадает в класс, когда вариантов несколько, спека между ними не
выбирает, а неверный выбор не падает, а молча меняет смысл данных. Перечисляй
места, где этот класс здесь живёт, а не переписывай определение. Таких мест нет
вовсе — так и напиши: `deep` тогда не запускается никогда, и это законное
состояние.
### Недоступно проверке ### Недоступно проверке
**Не проверит ни один проход** — принципиальная граница; по факту промаха не **Не проверит ни один проход** — принципиальная граница; по факту промаха не
+1 -1
View File
@@ -44,7 +44,7 @@ OK, DRIFT, USAGE, ENV, INTERNAL = 0, 1, 2, 3, 4
# Дом раскладки — «Модель по проходу» в review-pipeline/SKILL.md; здесь её # Дом раскладки — «Модель по проходу» в review-pipeline/SKILL.md; здесь её
# механизация. Порядок цветов — порядок стоимости прогона. # механизация. Порядок цветов — порядок стоимости прогона.
PALETTE = {"sonnet": "green", "opus": "yellow", "fable": "red"} PALETTE = {"sonnet": "green", "opus": "yellow"}
SKILL_KEYS = {"name", "description"} SKILL_KEYS = {"name", "description"}
AGENT_KEYS = {"name", "description", "tools", "model", "color"} AGENT_KEYS = {"name", "description", "tools", "model", "color"}