Два независимых сабагента на av-dev-pm и av-dev-pipeline. Две находки нашли оба. Главная — моя же перестановка закрытия за коммит сломала reopen и батч. close печатал «дорога назад из git», а reopen искал коммит удаления, которого в новом порядке ещё нет: шаг 11 последний, учёт остаётся незакоммиченным. Проверено прогоном — отказ кодом 2 на свежезакрытой задаче. Тем же грязным деревом ломались rebase и worktree remove в батче: каждая закрывшая задачу ветка уехала бы в провалившиеся. Починено с обеих сторон: reopen берёт текст из HEAD, если коммита удаления нет, а шаг 11 коммитит учёт вторым коммитом. Вторая — канонический пример docs/.pm.json убивал tasks.py. Четыре документа показывали ключ tasks.sections, которого скрипт не знает: неизвестный ключ это код 3 на любой команде. Проект, заведённый по канону дословно, остался бы без работы с задачами, а docs.py при этом печатал «канон соблюдён». Секции живут в заголовках индекса и второго дома не получают. Остальные восемнадцать: init писал конфиг в упразднённый .tasks.json; looks_like_tasks не видел переименованный индекс; урожай спринта терял автотег после sprint close; ответ на вопрос по инструкции оставлял задачу незабираемой; adopt требовал недостижимого зелёного; путь отчёта триажа не переживал archive; review-specs не имел режима для стыка после слияния; три остатка «шаг 9а» несли предкоммитную позицию закрытия; sprint.md отрицал сам себя в пункте «Сделана». Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
462 lines
41 KiB
Markdown
462 lines
41 KiB
Markdown
---
|
||
name: review-pipeline
|
||
description: Конвейер ревью изменения — детерминированный гейт, сверка с дельта-спеками в обе стороны, враждебные постановки и эксплуатационный постмортем, независимая реализация по триггеру, архитектура и обязательный триаж. Проходы гонятся последовательно; параллельно — только по явной просьбе и с явно названным набором. Проектная специфика приходит из документов канона 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](references/project-facts.md). Прочитай её до
|
||
раздачи заданий; там же таблица поразрядной деградации.
|
||
|
||
**Деградация поразрядная, а не всё-или-ничего.** Документа нет — деградирует то,
|
||
что из него читалось, и только оно: нет `docs/security.md` — слабеет
|
||
`adversary`; нет `docs/research/` — числа неизвестны трём проходам; нет
|
||
инвариантов в `CLAUDE.md` — `critical` по основанию «нарушен инвариант проекта»
|
||
не присваивается никем. Каждый проход пишет **свою** строку в границы покрытия, с
|
||
**причиной**; триаж сводит их и не сливает в одну.
|
||
|
||
**Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой
|
||
и предложи скилл `av-dev-pm:canon`: одна операция на проект против деградации на
|
||
каждой задаче. Прогон при этом не останавливается.
|
||
|
||
## Что получает каждый проход
|
||
|
||
Задание любому проходу состоит из шести вещей:
|
||
|
||
- **его блок вопросов** из «Вопросы к проходам» в `docs/review.md`, если он там
|
||
есть, — **дословно**. Блок адресован проходу поимённо и выведен из промаха
|
||
этого проекта; заставлять девять charter'ов самим ходить за ним значит
|
||
получить, что за ним ходят двое. Проход отвечает на такие вопросы явно,
|
||
дополнительно к обязательным;
|
||
- **контракт находок** — путь к
|
||
[references/finding-contract.md](references/finding-contract.md) (в
|
||
установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/review-pipeline/references/`);
|
||
- **изменение** — идентификатор change и путь к его дельта-спекам;
|
||
- **база диффа**;
|
||
- **профиль и режим** прогона — чтобы проход знал, что писать в границы покрытия;
|
||
- **сужение**, если оно есть: конкретный узел, конкретная capability.
|
||
|
||
Чего проход **не** получает ни в каком режиме — выводов других проходов. См.
|
||
«Режим запуска».
|
||
|
||
## Модель по проходу
|
||
|
||
Следует из правила 2: чем больше работы делает детерминированный инструмент,
|
||
тем дешевле может быть модель; чем больше проход **порождает** критерий, тем
|
||
дороже. Модель задана во frontmatter каждого агента, менять её здесь не нужно.
|
||
|
||
| Модель | Проходы | Почему |
|
||
|---|---|---|
|
||
| `sonnet` | gate, code, ops | вход структурный, критерий записан заранее |
|
||
| `opus` | specs, adversary, rubric, reimpl | суждение без опоры на инструмент |
|
||
| `fable` | triage, architecture | ошибка распространяется дальше самой находки |
|
||
|
||
**Самая дорогая модель — только двум проходам, и это калибровка, а не
|
||
осторожность.** Замер: на первом же прогоне конвейера самые ценные находки дали
|
||
`opus`-проходы — сверка спек дала 13 находок с оракулами, а проход про
|
||
идиоматичность (впоследствии упразднённый) — три эксперимента против драйвера БД
|
||
с воспроизведёнными числами. Разницы в пользу более дорогой модели на
|
||
опиниативных проходах не обнаружилось — значит платить за неё там не за что.
|
||
|
||
Двое, у кого она остаётся, отобраны по одному признаку: **их ошибка
|
||
распространяется дальше собственной находки.**
|
||
|
||
- `triage` — через него проходит всё, что оркестратор реализует **молча**:
|
||
ложноположительная находка становится кодом, потерянный `critical` — дефектом.
|
||
Ошибка триажа дороже ошибки любого отдельного прохода.
|
||
- `architecture` — запускается редко (только `deep` и `design`), потолок в
|
||
3 находки делает его дешёвым по выходу, а находка на предложении стоит абзаца
|
||
против переписывания на готовом коде. Дёшево × высокое плечо.
|
||
|
||
`reimpl` намеренно **не** в этом списке, хотя он самый ценный из generative: его
|
||
стоимость определяется объёмом вывода (он пишет реализацию целиком), так что
|
||
дорогая модель множит самый большой счёт. Ценность же его — в **независимости**
|
||
взгляда, а не в мощности модели.
|
||
|
||
**Самая дешёвая модель не используется ни на одном проходе, и это не экономия
|
||
наоборот.** Дешёвая модель на опиниативном проходе даёт правдоподобные находки,
|
||
которые триаж обязан опровергать оракулом, — а это самая дорогая операция
|
||
конвейера. Механизируемая же работа здесь вынесена **ниже** модели: гейт,
|
||
покрытие диффа, карта проекта — это скрипты проекта, они стоят ноль токенов.
|
||
Дешёвому проходу просто не осталось работы.
|
||
|
||
Экономия достигается не понижением модели, а **непуском прохода**: `quick` —
|
||
четыре прохода, `deep` — семь-восемь. Правило выбора профиля и есть главный
|
||
рычаг стоимости.
|
||
|
||
## Профили
|
||
|
||
| Профиль | Когда | Стадии | Проходов |
|
||
|---|---|---|---|
|
||
| `quick` | багфикс, локальная правка, доки | 0, 1, 5 | 4 |
|
||
| `standard` | новая функциональность в существующем пакете | 0, 1, 2, 5 | 6 |
|
||
| `deep` | новый пакет, изменение публичного контракта, миграция схемы, трогает инварианты проекта | 0, 1, 2, 3, 4, 5 | 7–8 |
|
||
| `design` | **до кода**, на предложении | specs + rubric + architecture (см. ниже) | 3 |
|
||
|
||
**Состав сверяется по этой таблице до коммита.** Реестр из трёх-восьми пунктов
|
||
проверяется взглядом — и это единственная защита от промаха, который уже
|
||
случился: пропуск прохода **не отличим от прохода без находок** (гейт зелёный,
|
||
спеки сошлись, отчёт выглядит полным), а заметить его мог бы только триаж,
|
||
который сам заполняется тем, что ему подали. Отчёт обязан перечислять запущенные
|
||
проходы **поимённо и с исходом**; непущенный идёт строкой «не запускался» в
|
||
границы покрытия, а не отсутствует. Цена молчащего пропуска измерена: семь
|
||
находок и отдельная задача на их дозакрытие.
|
||
|
||
Правило выбора профиля — **по факту изменения, не по ощущению важности**:
|
||
|
||
- есть миграция схемы, новый пакет, изменение публичного контракта (API,
|
||
протокол, формат на диске) или трогается правило, определяющее идентичность и
|
||
слияние данных → `deep`;
|
||
- иначе меняется поведение, видимое снаружи (эндпоинт, форма ответа, код ответа,
|
||
формат лога) → `standard`;
|
||
- иначе → `quick`.
|
||
|
||
Что именно в этом проекте считается публичным контрактом и какие пути означают
|
||
`deep`, проект может уточнить в `docs/review.md`, разделе настройки конвейера. Это
|
||
**уточнение**, а не отмена: не записано — работает список выше.
|
||
|
||
Профиль объявляется в отчёте. Понижение профиля — решение оркестратора, и оно
|
||
попадает в границы покрытия строкой «профиль понижен до X, потому что …».
|
||
|
||
## Режим запуска: параллельно или последовательно
|
||
|
||
Профиль отвечает «какие проходы», режим — «как их запускать». Стадии всегда идут
|
||
по порядку номеров; выбор касается только проходов **внутри** стадии.
|
||
|
||
| Режим | Как | Когда |
|
||
|---|---|---|
|
||
| **последовательно** (умолчание) | по одному, следующий стартует после отчёта предыдущего | всегда, пока не попросили иначе |
|
||
| **параллельно** | названные проходы — одним сообщением | только по явной просьбе **и** с явно названным набором |
|
||
|
||
**Умолчание — последовательно, и его не надо обосновывать.** Обосновывается
|
||
отступление.
|
||
|
||
**Параллельный режим включается при двух условиях сразу**, и второе так же
|
||
обязательно, как первое:
|
||
|
||
1. **о нём попросили явно** — «гони параллельно», а не «сделай побыстрее»;
|
||
2. **названо, что именно гнать параллельно** — поимённый набор проходов
|
||
(«`specs` и `code` параллельно») или стадия целиком («стадию 1 параллельно»).
|
||
|
||
Просьба без набора — **не основание**: гоним последовательно и одной строкой
|
||
говорим, что набор не был назван. Это не придирка к формулировке. Параллелить
|
||
можно ровно то, что не мешает друг другу, а знание об этом лежит у того, кто
|
||
просит: он видит, занята ли машина, и ждёт ли он от прогона замеров. Домысливать
|
||
набор за него — значит принять решение, которое он оставил себе.
|
||
|
||
Почему умолчание именно такое:
|
||
|
||
- **Замеры.** Проходы `adversary` и `ops` доказывают находки числами: время
|
||
удержания блокировки против её таймаута, пик кучи против размера тела, темп
|
||
роста файлов журнала, длительность транзакции. Два меряющих прохода на одной
|
||
машине соревнуются за диск, CPU и за саму СУБД и выдают числа, которые не
|
||
воспроизведутся. Это не гипотеза: правило выведено из находок, целиком
|
||
державшихся на таких замерах, — у каждого проекта они свои и лежат в журнале
|
||
`docs/review.md`. Число, снятое под конкурентную нагрузку от соседнего
|
||
прохода, — это находка с испорченным оракулом, а её опровержение стоит дороже
|
||
всего выигрыша от параллельности.
|
||
- **Машина одна.** Рядом идёт задача, поднят сервис, гоняется гейт или дорогая
|
||
проверка проекта.
|
||
- **Ранний выход** возможен только при последовательном прогоне (см. ниже).
|
||
- **Разбор самого конвейера.** Когда выясняется, почему проход чего-то не нашёл,
|
||
порядок и изоляция важнее скорости.
|
||
|
||
Если параллельный режим всё же включён, в границы покрытия идёт строка: какие
|
||
проходы шли разом и что замеры, снятые в этом прогоне, как оракул слабее.
|
||
|
||
**Чего режим не меняет — и это не подлежит обсуждению.** Проход **не видит**
|
||
находок других проходов ни в каком режиме. «Последовательно» значит «по
|
||
очереди», а не «следующий читает предыдущего». Вся ценность конвейера держится
|
||
на декорреляции: под всеми ролями одна модель с одними априорными, и стоит
|
||
показать ей чужой вывод — она согласится. Согласие нескольких проходов и так не
|
||
повышает `confidence` (см. «Честный предел»); согласие **наведённое** ещё и
|
||
маскируется под независимое подтверждение. Единственный, кто видит всё, — триаж,
|
||
и это его работа.
|
||
|
||
**Ранний выход** (последовательный режим делает его возможным — это его побочная
|
||
выгода, а не повод его выбирать). Допустимо остановить прогон, не докатив
|
||
остаток, ровно в одном случае: находка требует **переделки формы** изменения, и
|
||
остальные проходы будут смотреть на код, которого через час не станет. Тогда:
|
||
|
||
- прогон останавливается, находка чинится, конвейер запускается **заново с
|
||
нулевой стадии** — а не «доезжает» остатком по старому коду;
|
||
- незапущенные проходы идут в границы покрытия строкой «не запускался: прогон
|
||
остановлен на <проход> из-за <находка>», поимённо;
|
||
- триаж запускается только на полном прогоне. Отчёт триажа по половине проходов
|
||
выглядит полным, потому что агрегирует всё, что ему подали, — это тот же
|
||
молчащий пропуск, что и в разделе «Профили».
|
||
|
||
Ранний выход по находке, которая чинится в пределах существующей формы
|
||
(`Действие: инлайн`), **не делается**: дешевле дособрать все находки и починить
|
||
пачкой, чем гонять конвейер дважды.
|
||
|
||
Режим объявляется в отчёте наравне с профилем, и если он **параллельный** — с
|
||
причиной и составом. Последовательный объявляется одним словом.
|
||
|
||
## Стадия 0 — Gate (обязательна во всех профилях)
|
||
|
||
Агент `review-gate`. Запускает команду гейта из семантики гейта в `CLAUDE.md` и
|
||
интерпретирует вывод.
|
||
|
||
**Пока гейт красный — опиниативные проходы не запускаются.** Оркестратор чинит и
|
||
перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки
|
||
(гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не
|
||
блокирует.
|
||
|
||
Гейт возвращает не только «зелено/красно», но и находки класса **отсутствующая
|
||
верификация**: изменённые строки без покрытия, конкурентность без теста с
|
||
параллельным доступом, флаки-тест (не ниже `major`), недоступный инструмент.
|
||
|
||
Шаги выбираются по изменённым файлам: правка документации не гоняет тесты,
|
||
линтеры и детектор гонок. Пропуск при этом не молчит — он виден в сводке с
|
||
причиной и уезжает в границы покрытия, как и любой другой `SKIP`.
|
||
|
||
Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу
|
||
запрещено списывать такой отказ в мелочь.
|
||
|
||
## Стадия 1 — Conformance (обязательна во всех профилях)
|
||
|
||
Два applicative-прохода: оба применяют **записанный** критерий, оба дешёвые.
|
||
Замеров они не делают и потому безобиднее прочих, если параллельный режим
|
||
попросят с их именами; сами по себе идут по очереди, как и все.
|
||
|
||
- `review-specs` — критерий взят из **дельта-спек предлагаемого изменения**, а не
|
||
из proposal, сообщения коммита или описания задачи. Сверка двунаправленная;
|
||
направление `code → spec` важнее.
|
||
- `review-code` — критерий взят из конвенций проекта, каталог
|
||
`docs/conventions/`. Берётся только та их часть, которая **не выражается
|
||
правилом**: механизируемое уже проверила стадия 0. Что именно механизировано,
|
||
перечисляет `conventions/README.md` — повторять это проходом вредно.
|
||
|
||
Recall обоих равен длине их источника — это и есть предел applicative-проходов,
|
||
ради которого существует стадия 2.
|
||
|
||
## Стадия 2 — Adversarial и operational (`standard`, `deep`)
|
||
|
||
Два прохода:
|
||
|
||
- `review-adversary` — находка есть **построенный путь**, а не свойство;
|
||
- `review-ops` — постмортем от симптома у владельца сервиса к строке кода.
|
||
|
||
**Эту пару параллелить не стоит даже по просьбе — переспроси.** Оба доказывают
|
||
находки замером, и оба меряют одно и то же железо. Запущенные разом, они портят
|
||
числа друг другу, а испорченный оракул хуже отсутствующего: находка выглядит
|
||
доказанной. Если их всё же назвали в параллельном наборе — выполняй, но скажи в
|
||
границах покрытия, что числа этого прогона сняты под соседней нагрузкой.
|
||
|
||
**Эта стадия зарабатывает больше всех остальных вместе, и потому стоит в
|
||
`standard`, а не только в `deep`.** Измерено на пяти задачах подряд: враждебный
|
||
проход дал пять из семи выживших находок дозапуска (включая обе верхние);
|
||
эксплуатационный — единственный, кто нашёл, что откат бинаря поверх новой схемы
|
||
стартует молча. Оба несут внешний оракул по построению: один обязан путь
|
||
**прогнать**, второй смотрит ось времени и эксплуатации, которую не смотрит
|
||
никто другой.
|
||
|
||
Материал берётся из документов: `docs/security.md` — враждебному,
|
||
`docs/architecture.md`, `docs/research/` и `docs/database.md` —
|
||
эксплуатационному. Что с чем сшивать и почему — [project-facts.md](references/project-facts.md),
|
||
раздел «Сшивать обязаны проходы». Без этих документов стадия вырождается в общие
|
||
места.
|
||
|
||
## Стадия 3 — Independent reimplementation (`deep`, по триггеру)
|
||
|
||
- `review-reimpl` — пишет свою реализацию, не открывая существующую, затем
|
||
диффит по решениям. **Запускается по триггеру, а не всегда:** изменение вводит
|
||
новое правило идентичности, слияния или разбора (проектная формулировка
|
||
триггера — в `docs/review.md`, если записана). Это самый дорогой проход конвейера
|
||
(его счёт определяется объёмом вывода — он пишет реализацию целиком), а вне
|
||
этого триггера независимый взгляд в значительной мере уже дал профиль `design`:
|
||
код писался под его находки. Триггер выбран по факту: единственный раз, когда
|
||
триаж назвал отсутствие `reimpl` дырой покрытия, — это была задача с новым
|
||
правилом слияния сущностей.
|
||
|
||
## Стадия 4 — Global (`deep`, `design`)
|
||
|
||
Агент `review-architecture`. Получает **вход шире диффа**: дерево пакетов с
|
||
назначением, граф внутренних зависимостей, инвентарь существующих концепций.
|
||
Команду, которая это готовит, даёт раздел команд `CLAUDE.md`; нет команды —
|
||
проход собирает карту сам и говорит об этом в границах покрытия.
|
||
|
||
Главный вопрос — концептуальная целостность и **второй способ** делать то, что
|
||
уже делается. Он же и оправдывает проход: на задаче про пересборку архитектурный
|
||
проход нашёл, что новый код был **вторым проигрывателем журнала** со своим
|
||
порядком. Второй обязательный вопрос — **что опытный человек отсюда удалил бы**:
|
||
слой с единственной реализацией, интерфейс ради мока, незапрошенная
|
||
конфигурируемость, подстраховка поверх подстраховки. Потолок — 3 находки плюс
|
||
секция «дешевле переделать до мерджа».
|
||
|
||
## Стадия 5 — Triage (обязательна)
|
||
|
||
Агент `review-triage`. Единственный, кто агрегирует. Получает сырые выводы всех
|
||
проходов, `git diff`, профиль, режим и **список запущенных проходов**; возвращает
|
||
финальный отчёт.
|
||
|
||
Без триажа проходы дают порядка сорока замечаний при единицах существенных.
|
||
Потребитель здесь — оркестратор, который **молча реализует** всё, что прочитал:
|
||
цена нетриажированного отчёта — не потерянное время человека, а разросшийся от
|
||
вкусовщины код.
|
||
|
||
Порядок: дедупликация по причине → оракул для всего `critical`/`major` →
|
||
понижение неподтверждённого до гипотезы → отсев вкусовщины → ранжирование по
|
||
ущербу × вероятности → потолок 7 пунктов в основном списке.
|
||
|
||
## Профиль `design` — до кода
|
||
|
||
Запускается на шаге ревью спек (шаг 4 скилла `av-dev-pipeline:task-pipeline`),
|
||
когда change уже
|
||
имеет `proposal.md` и дельта-спеки, но кода ещё нет. Состав:
|
||
|
||
1. `review-specs` в режиме «дизайн ДО кода»;
|
||
2. `review-rubric`, фаза 1 без фазы 2: рубрика на задуманный узел становится
|
||
приёмочными критериями и уезжает в `tasks.md`;
|
||
3. `review-architecture` на предложении: вводит ли change новое понятие, можно ли
|
||
выразить существующими — **включая конструкции стандартной библиотеки**, — не
|
||
появляется ли второй способ. Вопрос «не изобретаем ли то, что уже есть в
|
||
библиотеке» живёт здесь;
|
||
4. вопрос автору дизайна: **«предложи три формы решения и назови компромисс
|
||
каждой»** — если ответ показывает, что рассматривалась одна, это находка.
|
||
|
||
Смысл профиля: архитектурная находка на готовом коде стоит переписывания и
|
||
поэтому игнорируется; та же находка на предложении стоит абзаца обсуждения.
|
||
|
||
`rubric` живёт **только** в этом профиле. Судить код по критерию, под который он
|
||
писался, — корреляция по построению; те же 8–12 свойств уже лежат приёмочными
|
||
критериями в `tasks.md`.
|
||
|
||
## Контракт находок
|
||
|
||
Единый для всех проходов — [references/finding-contract.md](references/finding-contract.md).
|
||
Коротко: заголовок через **последствие**, обязательные поля `Файл`, `Severity`,
|
||
`Confidence`, `Оракул`, `Последствие`, `Предложение`, `Найдено проходом`.
|
||
`critical` без оракула или построенного пути не существует. Находка без поля
|
||
«Последствие» не выводится вовсе.
|
||
|
||
Каждый проход завершает вывод блоком `## Coverage of this pass`.
|
||
|
||
## Что происходит с находками дальше
|
||
|
||
- Оркестратор чинит помеченное `Действие: инлайн` и **не логирует мелочь**.
|
||
- `Действие: развилка` — вопросом с вариантами и ценой каждого туда, где проект
|
||
держит вопросы (это знает вызвавший пайплайн, а не конвейер). Оркестратор не
|
||
останавливается: он урезает изменение до остатка и доводит его.
|
||
- Находка не для этого мерджа, но реальная (отложенный `major`, развилка,
|
||
решённая «потом»), — не теряется, но **и не заводится здесь**. Конвейер отдаёт
|
||
её **списком урожая** в отчёте: формулировка, оракул, провенанс (какой проход,
|
||
какой change). Заведение задач принадлежит тому, кто ведёт задачи проекта, —
|
||
у него свой формат, своя нарезка и свои правила дублей. Мелочь класса `nit`
|
||
идёт в урожай одной пачкой, а не записью на находку.
|
||
- `Promote candidates` — по процедуре [references/promote.md](references/promote.md):
|
||
находка → конвенция → правило линтера → **удаление формулировки из конвенций**.
|
||
Третий шаг обязателен.
|
||
- Дефект, проскочивший ревью и всплывший позже, идёт в журнал проекта
|
||
([references/review-journal.md](references/review-journal.md)) — сразу, не
|
||
ретроспективно: теряется именно причина непоймания.
|
||
- **Отчёт триажа сохраняется вместе с изменением** — `openspec/changes/<id>/review/`.
|
||
Он единственное, по чему потом видно, что было найдено и что из этого не
|
||
заведено: нулевой урожай при непустом отчёте виден сразу.
|
||
**Вместе с изменением он и переезжает:** после `opsx:archive` его адрес —
|
||
`openspec/changes/archive/<id>/review/`. Кто ищет отчёт после архивации (батч
|
||
на финальной сверке, приёмщик на сессии), смотрит **оба** пути; «отчёта нет»
|
||
объявляется, только когда пуст и архивный, иначе самый дорогой сценарий
|
||
«состав ревью неизвестен, гоняем заново» срабатывает на каждой доведённой
|
||
задаче.
|
||
|
||
## Честный предел
|
||
|
||
Модель воспроизводит медиану публичного кода, смещённую к популярному и
|
||
туториальному: отсюда тяга к интерфейсам ради интерфейсов, лишним мокам и
|
||
конфигурируемости, которую никто не просил. **«Идиоматично» и «распространено» —
|
||
разные вещи**; проходы обязаны различать их и опираться на поимённое положение
|
||
гайда, а не на ощущение частотности.
|
||
|
||
Согласие нескольких проходов — **не подтверждение**: это один источник,
|
||
высказавшийся несколько раз. Совпадение повышает приоритет, но не `confidence`.
|
||
|
||
Что недоступно **этому** проекту принципиально — перечисляет «Недоступно
|
||
проверке» в `docs/review.md`, и оба его подраздела целиком уезжают в границы
|
||
покрытия.
|
||
Независимо от проекта недоступно:
|
||
|
||
- поведение внешних систем в их будущих версиях;
|
||
- реальный профиль нагрузки и то, что на самом деле лежит в данных;
|
||
- завязка внешних потребителей на текущую форму ответа;
|
||
- суждение «этой функциональности не должно существовать».
|
||
|
||
Отдельно и честно: **поимённая сверка с положениями стайлгайдов языка не
|
||
задаётся ни одним проходом.** Проход про идиоматичность упразднён, его способные
|
||
части переселены (эксперимент против поведения библиотеки и драйвера — в `ops`,
|
||
вопрос 8; «не изобретаем ли то, что уже есть в библиотеке» — в `architecture`,
|
||
вопрос 1), но различение «идиоматично против распространено» теперь не спрашивает
|
||
никто. Класс обратимый — портит форму кода, не данные, — и его надо признавать в
|
||
границах покрытия, а не считать проверенным.
|
||
|
||
Это и есть причина, по которой конвейер готовит ревью, а не заменяет его.
|
||
|
||
## Ссылки
|
||
|
||
- [references/project-facts.md](references/project-facts.md) — что нужно проходу
|
||
и где это лежит в документах проекта; таблица поразрядной деградации.
|
||
- Skill `av-dev-pm:canon` — приведение проекта к канону документов.
|
||
- [references/finding-contract.md](references/finding-contract.md) — контракт находок.
|
||
- [references/promote.md](references/promote.md) — промоут находка → конвенция → правило → удаление.
|
||
- [references/calibration.md](references/calibration.md) — калибровка инъекцией, вердикты keep/retune/drop.
|
||
- [references/review-journal.md](references/review-journal.md) — журнал проскочивших дефектов.
|