Синк документации делил правки по документам, а делить их надо по роду. Отражение сделанного (вливание дельт, миграция, компонент в обзоре) пишется молча: без правки документ станет ложным. Новая запись и новая норма — ADR, конвенция, записка разведки, инвариант, периметр, дефект в журнале — только предлагаются, а пишет их третий такт шага 6 после слова человека. Реплика при этом одна на весь хвост: вопрос про урожай ревью переехал с шага 5 на шаг 6 и слился с предложениями синка — решение одно, «что из найденного переживёт задачу». Плановых стопов в сценарии решения стало ровно два, и оба про решения человека. Сверка документов получила счётчик: doc-healthcheck оставляет след ключом [healthcheck] last в .av-dev.toml, синк считает по нему задачи с прошлого прогона и говорит строкой. Прежде признак «десяток задач» держался в памяти, то есть не срабатывал. Журнал — тема 78.
981 lines
90 KiB
Markdown
981 lines
90 KiB
Markdown
---
|
||
name: code-review
|
||
description: "Конвейер ревью изменения, устроенный по темам: документ проекта либо заводит тему ревью, либо питает чужую тему источником, либо процессный и в ревью не читается вовсе. Ядро тем — requirements, autotests, conventions, architecture, security, operations; список тем открытый, свои темы проект заводит документом. Состав прогона постоянный, метки у него нет: гейт (autotests), сверка со спекой (specs), разбор кода и конвенций (code), триаж; приёмник тем (basics) идёт, когда у проекта есть свои темы. Цикл задачи проверяет корректность и механику против записанного критерия — дельта-спеки, конвенции, инварианты CLAUDE.md, вывод инструментов. Темы риска и устройства — security, operations, architecture — закрыты в цикле только сверкой с записанными инвариантами: их разбор, доказательство запуском и суждение о форме решения живут в скилле av-dev:code-deep-review, который идёт по области кода и время от времени. Порядок прогона — граф зависимостей: гейт открывает проходы с мнением, триаж — единственный сток. Находки по умолчанию чинятся инлайн и молча; человеку уходит только необратимое и то, что меняет дельта-спеки, а задачи из урожая заводятся по его слову. Проектная специфика приходит из документов канона проекта. Вызывается из скилла av-dev:code-resolve после apply. Второй вызов идёт от сценария обслуживания: без change, фиксированным планом (autotests, operations, плюс conventions, если тронут код)."
|
||
---
|
||
|
||
# Конвейер ревью
|
||
|
||
Готовит ревью — **не заменяет его**. Потребитель отчёта — оркестратор, который
|
||
чинит код; человек читает только сводку, развилки и границы покрытия.
|
||
|
||
## Четыре правила, из которых всё следует
|
||
|
||
Если ситуация не покрыта инструкцией — решай по ним.
|
||
|
||
0. **Тема первична, проход вторичен.** Ревью проверяет **темы** — набор
|
||
направлений, который проект объявляет своими документами. Проход это только
|
||
способ закрыть тему на заданной глубине, и проходы меняются: переезжают в
|
||
другой скилл, сливаются, упраздняются. Если состав прогона считать списком
|
||
проходов, то уехавший проход уносит тему с собой **беззвучно** — отчёт честно
|
||
скажет «`ops` не запускался» и не скажет «эксплуатацию не смотрел никто», а
|
||
нужно второе. Проверено на живом переезде: `ops` и `adversary` ушли в
|
||
`av-dev:code-deep-review`, а темы `security` и `operations` остались в
|
||
конвейере — узко, сверкой с инвариантами внутри `code`, и это записано
|
||
строкой. Поэтому прогон описывается таблицей «тема → кто закрывает → против
|
||
чего», и таблица эта есть в каждом отчёте.
|
||
|
||
1. **Recall чек-листа равен длине чек-листа.** Проход, устроенный как «проверь
|
||
пункты 1..N», найдёт ровно перечисленное. Всё неявное — идиомы, форма
|
||
решения, «так не делают» — неперечислимо по определению: перечислимое уже
|
||
стало бы конвенцией. Отсюда деление проходов на **applicative** (применяют
|
||
заданный критерий) и **generative** (сперва порождают критерий или
|
||
альтернативу, потом сравнивают). Расширять чек-листы бесполезно; неявный слой
|
||
достают только generative-проходы.
|
||
2. **Ценность верификатора = наличие внешнего оракула × разведённость с
|
||
автором**, а не число ролей. Под всеми ролями одна модель с одними
|
||
априорными, вход у всех общий: седьмая роль почти не добавляет recall, но
|
||
линейно удорожает триаж. Иерархия надёжности: детерминированный инструмент >
|
||
агент, который его **запускает** и интерпретирует вывод > агент с чистым
|
||
мнением. Максимум работы переносим вниз.
|
||
3. **Отчёт без границ покрытия хуже отсутствия отчёта.** «Критичных проблем не
|
||
обнаружено» потребляет ощущение проверенности, ничего не гарантируя. Секция
|
||
границ покрытия обязательна и не сокращается — в том числе в докладе человеку.
|
||
|
||
## Предпосылки
|
||
|
||
Конвейер опирается на внешнюю обвязку и без неё работает не целиком. Проверь это
|
||
один раз, при установке плагина в проект:
|
||
|
||
- **OpenSpec — жёсткая предпосылка, а не опция.** Проход `review-specs` и
|
||
вызывающий скилл `av-dev:code-resolve` завязаны на дельта-спеки
|
||
(`openspec/changes/<id>/specs/*/spec.md`), на актуальные спеки
|
||
(`openspec/specs/`) и на `openspec validate --strict`. В проекте без OpenSpec
|
||
шаги, зовущие `opsx:explore` / `opsx:propose` / `opsx:apply` / `opsx:archive`,
|
||
упадут на «нет такого скилла», а `review-specs` останется без источника
|
||
требований. **Проект без OpenSpec этим конвейером не проверяется** — подключай
|
||
OpenSpec, а не понижай прогон: ветка деградации здесь не пишется, потому что
|
||
непроверенная ветка деградации хуже честного отказа. Заводить руками не надо:
|
||
этим владеет скилл `av-dev:code-openspec` — он заводит каталог и заменяет
|
||
пример в `config.yaml` настройкой. Его же зовут `av-dev:doc-init` на новом
|
||
проекте и `av-dev:canon` в режиме `adopt` — на переводимом.
|
||
**Предпосылка эта — про изменение поведения, а не про всякий прогон:**
|
||
сценарий обслуживания зовёт конвейер без change и без дельта-спек, и ни один
|
||
проход его плана на них не завязан. См. «Прогон без change».
|
||
- **Документы канона** — см. следующий раздел.
|
||
- **Проектные копии этих скиллов и агентов удаляются при установке.**
|
||
|
||
<!-- копия: проектные-копии из README.md -->
|
||
|
||
Голые имена в `.claude/skills/`: `resolve`, `review`, а у проектов прошлого
|
||
поколения ещё `task-pipeline`, `review-pipeline`, `task-batch`. С префиксом
|
||
проекта: `<проект>-task-pipeline`, `<проект>-review-pipeline`. Агенты:
|
||
`.claude/agents/<проект>-review-*.md`.
|
||
|
||
Две копии одного скилла расходятся, и побеждает та, что **короче названа**:
|
||
короткое имя разрешится в устаревшую проектную копию молча и без признаков
|
||
подмены.
|
||
|
||
<!-- /копия: проектные-копии -->
|
||
|
||
### Чего может не быть
|
||
|
||
**Копия.** Дом правила — `shared/absence.md` в репозитории плагина.
|
||
Правится дом, а не этот файл.
|
||
|
||
<!-- копия: отсутствие из av-dev/shared/absence.md -->
|
||
|
||
**Скилл не вправе считать раскладку проекта полной.** Части заводятся порознь и
|
||
живут порознь; каждая узнаётся своим следом:
|
||
|
||
| Чего нет | Как видно | Чего теперь не делает никто |
|
||
| --- | --- | --- |
|
||
| настройки av-dev | нет `.av-dev.toml` в корне | проект под процесс не заводился; версии нет, настроек нет |
|
||
| документы канона | нет `docs/` | проектную конкретику брать неоткуда — темы, инварианты, прецеденты |
|
||
| учёт работ | нет каталога задач | запись остаётся владельцу: назови её текстом в докладе |
|
||
| источник требований | нет `openspec/config.yaml` | цикл SDD не запускается: спеки не с чем сверять |
|
||
|
||
**Свой скилл зовётся полным именем** — `av-dev:canon`, `av-dev:task-track`,
|
||
`av-dev:code-review`. Короткое имя может разрешиться в устаревшую проектную
|
||
копию из `.claude/skills/`, и подмены не будет видно ни в докладе, ни в
|
||
поведении.
|
||
|
||
**Внешний плагин может не стоять.** Их два: `opsx:*` — цикл SDD, и
|
||
`av-dev-git:commit` — сообщения коммитов. Путь в дерево чужого плагина не
|
||
пишется никогда: `$CLAUDE_PLUGIN_ROOT` ведёт только в своё дерево, а
|
||
вычисленный от него путь к соседу либо не откроется, либо откроет чужую
|
||
установку. Нужен чужой справочник — зови владеющий им скилл, он прочитает его
|
||
сам.
|
||
|
||
**Отсутствие — исход, а не поломка.** Назови строкой доклада, чего теперь не
|
||
делает никто, и продолжай работу. Молчать нельзя: пропуск неотличим от
|
||
сделанного. Выдумывать обходной путь нельзя тоже.
|
||
|
||
**Присутствие узнаётся следом в проекте, а не объявлением.** Перечня того, что
|
||
здесь заведено, проект не ведёт — он разошёлся бы с действительностью молча.
|
||
|
||
<!-- /копия: отсутствие -->
|
||
|
||
Своих скиллов это касается ровно так же: `av-dev:code-review`,
|
||
`av-dev:code-resolve`, `av-dev:code-openspec` — подменяется короткое имя,
|
||
а не чужое.
|
||
|
||
## Темы, источники и процессные документы
|
||
|
||
Раньше здесь стояло плоское правило «каждый документ проекта — тема ревью». Оно
|
||
верно ровно наполовину, и потому вредно целиком: паспорт и схему хранилища
|
||
ревью читает, но темами они не являются, а журнал решений и журнал наблюдений
|
||
ревью изменения не нужны вовсе. Прогон, применявший правило буквально, обязан был
|
||
либо завести фантомные темы и продублировать ими работу настоящих, либо потерять
|
||
документ молча.
|
||
|
||
**Разрез один и проверяемый — тот же, что в каноне: можно ли по документу
|
||
сказать «в этом изменении сделано не так»?**
|
||
|
||
| Категория | Что конвейер с ней делает | Кто в ней |
|
||
|---|---|---|
|
||
| **тема** | заводит направление проверки и требует исполнителя | `conventions.*`, `security.*`, `architecture.*`, свои документы проекта |
|
||
| **источник темы** | читает как материал чужой темы, своей не порождает | `passport.*`, `database.*`, `CLAUDE.md`/`AGENTS.md`, `openspec/specs/` |
|
||
| **процессный документ** | не судит по нему изменение | `tasks/`, `docs/review.*`, `docs/adr.*`, `docs/research.*`, `.av-dev.toml` |
|
||
|
||
**Одна процессная запись всё же читается — `docs/review.*`.** В ней лежит
|
||
настройка самого конвейера: вопросы по темам, журнал дефектов, типовые узлы,
|
||
типовые ложноположительные. Проход, читающий её, читает **свою обвязку**, а не
|
||
критерий, по которому судит изменение. `adr.*`, `research.*` и `tasks/` не
|
||
открывает никто.
|
||
|
||
Дом канона этой раскладки — скилл `av-dev:canon`, раздел «Три категории
|
||
документов». Конвейер её **читатель**: категории и имена тем он берёт
|
||
оттуда и своих не заводит.
|
||
|
||
Отсюда то, ради чего правило и заведено: **`docs/` перестаёт быть просто
|
||
документацией и становится конфигурацией конвейера**. Проект настраивает ревью
|
||
тем, что пишет о себе, а не отдельным файлом настроек, который разошёлся бы с
|
||
документами. **Открыта при этом только категория `тема`** — две другие закрыты
|
||
и перечислены поимённо, поэтому документ, которого нет в раскладке канона,
|
||
однозначно своя тема проекта, а не «что-то непонятное».
|
||
|
||
Ядро — шесть тем, они есть у любого проекта, приведённого к канону. Форма дома
|
||
значения не имеет: `docs/security.md` и `docs/security/` — одна тема `security`.
|
||
|
||
| Тема | Дом | Вопрос темы |
|
||
|---|---|---|
|
||
| `requirements` | `openspec/specs/`, дельты change | делает ли код заказанное, и только его |
|
||
| `autotests` | `CLAUDE.md`: семантика гейта, команды | проверено ли машиной и хватает ли проверок |
|
||
| `conventions` | `docs/conventions.*` | написано ли так, как здесь пишут |
|
||
| `architecture` | `docs/architecture.*` + источник `passport.*` | цело ли устройство: понятия и границы |
|
||
| `security` | `docs/security.*` | что сделает недоверенный вход |
|
||
| `operations` | `docs/architecture.*`, раздел эксплуатации + источник `database.*` | что будет через неделю на проде |
|
||
|
||
**Три темы ядра дома в `docs/` не имеют, и это не пробел.** `requirements` живёт
|
||
в `openspec/`, `autotests` — в `CLAUDE.md`, `operations` — разделом внутри
|
||
`architecture.*`. Имя темы поэтому не выводится из имени файла, и обратно тоже:
|
||
`docs/passport.md` не заводит темы `passport`.
|
||
|
||
**`adr/` и `research/` прогон больше не открывает.** Раньше архитектурный проход
|
||
читал решения, а эксплуатационный и `specs` — числа. Цена решения записана в
|
||
каноне и повторяется здесь, потому что платит её конвейер: **расхождение
|
||
изменения с записанным решением прогоном не ловится**, это работа сверки
|
||
документации — скилл `av-dev:doc-healthcheck`.
|
||
Строка об этом обязательна в границах покрытия каждого прогона.
|
||
|
||
**Своя тема проекта бывает двух происхождений, и обе законны:** документ, который
|
||
проект положил в `docs/` и которого нет в раскладке канона, — и **тема, названная
|
||
директивой** `CLAUDE.md`/`AGENTS.md`, у которой документа нет вовсе. У второй дом
|
||
— сама директива; в остальном она ничем не отличается, и в раздаче идёт туда же.
|
||
Различать их приходится потому, что условие запуска приёмника тем звучит «есть ли
|
||
свои темы проекта», и тема без файла в `docs/` иначе не попала бы под это условие
|
||
никогда.
|
||
|
||
**Проектная тема закрывается `basics`**, и только она. Именных проходов конечное
|
||
число, а тем — сколько заведёт проект; приёмник обязателен, иначе открытость
|
||
списка была бы обещанием без механизма. Темы **ядра** он не держит вовсе:
|
||
`requirements` закрывает `specs`, `conventions` и технику — `code`, а риск и
|
||
устройство — тот же `code` сверкой с инвариантами. Отсюда правило состава:
|
||
**`basics` запускается тогда и только тогда, когда ему есть что принимать** — см.
|
||
«Состав прогона».
|
||
|
||
**Тема без дома — законное состояние и отдельная строка.** «Тема `operations`
|
||
заявлена, `docs/database.md` нет» читается иначе, чем «не смотрели». Деградация
|
||
поразрядная: нет дома — падает глубина этой темы, и только её.
|
||
|
||
Что именно проход читает по каждой теме — [references/project-facts.md](references/project-facts.md).
|
||
Отдельного файла-брифа при этом нет: пути известны, посредник не нужен, а второй
|
||
дом для тех же фактов разошёлся бы и выглядел актуальным.
|
||
|
||
**Документов канона нет вовсе** — проект не приведён к канону. Скажи это строкой
|
||
и предложи скилл `av-dev:canon`: одна операция на проект против деградации на
|
||
каждой задаче. Прогон при этом не останавливается.
|
||
|
||
## Что получает каждый проход
|
||
|
||
Задание собирается **по таблице тем** и состоит из шести вещей:
|
||
|
||
- **его темы** — какие темы он закрывает, у каждой **дом** (путь и раздел) и
|
||
**глубина**. Дом передаётся адресом, а не пересказом: проход, получивший
|
||
проинтерпретированный периметр, не заметит, что интерпретация неверна;
|
||
- **вопросы по его темам** из `docs/review.*`, если они там есть, — **дословно**.
|
||
Вопрос привязан к теме, а не к имени прохода, и потому переживает переезд
|
||
прохода между скиллами;
|
||
- **контракт находок** — путь к
|
||
[references/finding-contract.md](references/finding-contract.md) (в
|
||
установленном плагине — `${CLAUDE_PLUGIN_ROOT}/skills/code-review/references/`);
|
||
- **изменение** — идентификатор change и путь к его дельта-спекам;
|
||
- **база диффа**;
|
||
- **его глубина и режим** прогона — чтобы проход знал, что писать в границы
|
||
покрытия.
|
||
|
||
**Ступень 1 получает сверх этого исход гейта, прогнанного до ревью** — сводку,
|
||
путь к логам шагов и отпечаток дерева, — если вызывающий скилл его дал. Зачем и
|
||
что происходит при расхождении — «Ступень 1 — Автотесты».
|
||
|
||
Чего проход **не** получает ни в каком режиме — выводов других проходов. См.
|
||
«Порядок прогона».
|
||
|
||
## Модель по проходу
|
||
|
||
Модель выбирается **по цене ошибки прохода, а не по его роду**. Признак рабочий и
|
||
проверяемый: находка со ссылкой на записанный источник — строку спеки, цель в
|
||
манифесте, значение в конфиге — опровергается открытием файла, и дешёвая модель
|
||
ошибается здесь проверяемо; находка-суждение опровергается рассуждением, а
|
||
рассуждение стоит триажа или человека. Второй род ошибки — **пропуск**: он не
|
||
стоит ничего сегодня и не виден вовсе, и проход, у которого дороже пропустить,
|
||
держится наверху, даже будучи applicative.
|
||
|
||
Модель задана во frontmatter каждого агента, менять её здесь не нужно.
|
||
|
||
| Модель | Цвет | Проходы | Почему |
|
||
|---|---|---|---|
|
||
| `sonnet` | green | autotests, ops | вывод перечислим и сверяется механически |
|
||
| `opus` | yellow | specs, code, basics, triage, rubric, adversary, architecture | дорога ошибка — ложная либо пропущенная |
|
||
|
||
**В таблице стоят и проходы, которых в цикле нет.** `adversary`, `ops` и
|
||
`architecture` работают в скилле `av-dev:code-deep-review`, `rubric` зовут прямо
|
||
руками; раскладка «модель — цвет» общая для всех уставов плагина и проверяется
|
||
механически, поэтому дом у неё один, а не по скиллу.
|
||
|
||
**Цвет charter'а кодирует модель, а не роль прохода.** Это единственное
|
||
назначение цвета: список агентов читается взглядом, и по нему сразу видно, чем
|
||
платит прогон. Роль прохода из имени и так понятна, а цвет, розданный по ролям,
|
||
не отвечает ни на один вопрос, который задают во время прогона. Раскладка живёт
|
||
здесь и **проверяется механически** — цвет ставится один раз при заведении
|
||
charter'а, а модель потом двигает калибровка, и разъезжаются они молча.
|
||
|
||
**Моделей две, и верхняя из них — `opus`; выше неё конвейер не платит.** Замер:
|
||
на первом же прогоне самые ценные находки дали `opus`-проходы — сверка спек дала
|
||
13 находок с оракулами, а проход про идиоматичность (впоследствии упразднённый) —
|
||
три эксперимента против драйвера БД с воспроизведёнными числами. Разницы в пользу
|
||
модели **дороже** `opus` не обнаружилось ни на одном проходе, а прогон на ней
|
||
стоил заметно дольше и дороже — значит платить за неё не за что.
|
||
|
||
Четверо держатся наверху не за суждение, а по отдельным причинам, и их стоит
|
||
знать поимённо:
|
||
|
||
- `triage` — через него проходит всё, что оркестратор реализует **молча**:
|
||
ложноположительная находка становится кодом, потерянный `critical` — дефектом.
|
||
Ошибка триажа дороже ошибки любого отдельного прохода.
|
||
- `specs` — по устройству applicative, но направление `code → spec` требует
|
||
заметить **отсутствие**: тихий фолбэк, самодеятельный дефолт, проглоченную
|
||
ошибку. Здесь дорог пропуск, а не ложная находка.
|
||
- `code` — единственный, кто читает код **как код**, и с уходом тяжёлых проходов
|
||
он же единственный, кто смотрит на риск и устройство. Его пропуск это дефект в
|
||
проде, и он не оставляет следа ни в отчёте, ни в границах покрытия. По той же
|
||
причине, что `specs`, и это дороже всего в конвейере: проход идёт на каждой
|
||
задаче.
|
||
- `basics` — держит темы, которые проект завёл сам, то есть ровно те, о которых
|
||
плагин ничего не знает. Ошибиться на чужой теме дешёвой моделью проще всего:
|
||
критерий приходит текстом документа, а не перечнем.
|
||
|
||
**Самая дешёвая модель не используется ни на одном проходе, и это не экономия
|
||
наоборот.** Дешёвая модель на проходе с мнением даёт правдоподобные находки,
|
||
которые триаж обязан опровергать оракулом, — а это самая дорогая операция
|
||
конвейера. Механизируемая же работа здесь вынесена **ниже** модели: гейт,
|
||
покрытие диффа, карта проекта — это скрипты проекта, они стоят ноль токенов.
|
||
Дешёвому проходу просто не осталось работы.
|
||
|
||
Экономия достигается **не понижением модели, а тремя другими рычагами**, и все
|
||
три применяются к каждому проходу с мнением, а не к одному избранному.
|
||
|
||
1. **Непуск.** Тяжёлые проходы в цикле не запускаются вовсе — они живут в
|
||
`av-dev:code-deep-review`; приёмник тем не идёт, когда своих тем у проекта
|
||
нет. Что при этом перестаёт проверяться, названо поимённо и идёт в границы
|
||
покрытия.
|
||
2. **Вход.** `basics` идёт на верхней модели, но с узким входом: дифф и его
|
||
окрестности, без карты проекта. Карта проекта и вход шире диффа не даются в
|
||
цикле никому — это цена глубокого прогона, а не задачи.
|
||
3. **Потолок.** Он есть у каждого прохода с мнением и напечатан: `basics` — 4
|
||
находки; `code` — 4 конвенционных и 1 на все три темы риска и устройства
|
||
разом, у технической половины потолка нет; `specs` — потолка нет; триаж — 7 в
|
||
основном списке. Двум половинам его не ставят намеренно: пропуск дефекта и
|
||
пропуск расхождения со спекой стоят дороже длинного списка.
|
||
|
||
Все три раньше зависели от метки и потому на каждой задаче считались заново.
|
||
Теперь они постоянные, и проход знает свой потолок до того, как получил задание.
|
||
Проход без потолка выдаёт столько находок, сколько нашёл поверхностей, — а это
|
||
ровно тот механизм, из-за которого был снят проход независимой реализации:
|
||
**счёт определялся объёмом вывода**. Потолок ставится не ради краткости отчёта, а
|
||
против этого.
|
||
|
||
**Потолок обязан быть объявлен, когда он сработал.** Проход, срезавший находки
|
||
до потолка, говорит об этом строкой в своих границах покрытия: сколько осталось
|
||
за срезом и какого рода. Молчащий срез неотличим от «больше не нашлось» — это тот
|
||
же класс молчащего пропуска, что и непущенный проход.
|
||
|
||
## Состав прогона — постоянный
|
||
|
||
**Ступени нумерованы и наружу не выходят.** Прогон ревью один, и зовёт его
|
||
`av-dev:code-resolve` после того, как код написан; членение внутри прогона —
|
||
ступени, и знать их снаружи не нужно. Перечень осей процесса целиком —
|
||
[shared/axes.md](../../shared/axes.md).
|
||
|
||
**Состав не выводится ни из чего: он один и тот же на всякой задаче.** Гейт,
|
||
сверка со спекой, разбор кода, триаж; приёмник тем — когда у проекта есть свои
|
||
темы. Прежде состав выбирала **метка** `small`/`medium`/`large`, которую считал
|
||
отдельный проход по двум осям — размеру и сложности. Метки больше нет, и вместе с
|
||
ней ушли разметка, матрица выбора, правило «спорное решается вниз» и доли по
|
||
журналу.
|
||
|
||
**Цикл задачи проверяет корректность и механику, и это его определение.**
|
||
Вопрос «делает ли код заказанное и не сломается ли он сам» отвечается против
|
||
**записанного** критерия: дельта-спеки, конвенции, инварианты `CLAUDE.md`, вывод
|
||
инструментов. Вопрос «то ли это решение» здесь не задаётся вовсе: он стоит
|
||
человеку разговора, а место разговора назначено — чекпоинт до кода, где форму
|
||
решения одобряет человек, и скилл `av-dev:code-deep-review`, где находки
|
||
разбирают по одной.
|
||
|
||
Отсюда таблица тем — единственная и без вариантов:
|
||
|
||
<!-- дом: тема-глубина -->
|
||
|
||
| Тема | Кто закрывает | Против чего и как |
|
||
|---|---|---|
|
||
| `autotests` | `autotests` | запуск: гейт проекта и логи его шагов |
|
||
| `requirements` | `specs` | разбор: дельта-спеки change, сверка в обе стороны |
|
||
| `conventions` | `code` | разбор: дома конвенций проекта |
|
||
| техника | `code` | разбор: дефект, который сработает сам |
|
||
| `security`, `operations`, `architecture` | `code` | **сверка с записанными инвариантами `CLAUDE.md`** — и только |
|
||
| тема проекта | `basics` | разбор: дом темы против диффа |
|
||
|
||
<!-- /дом: тема-глубина -->
|
||
|
||
**Три темы риска и устройства закрыты узко, и это названо прямо.** Свойство,
|
||
которого нет в инвариантах, в цикле не спросит никто: ни сценарием, ни чтением
|
||
дома темы. Это самая крупная граница покрытия конвейера, она идёт строкой в
|
||
каждом отчёте, и снимает её не прогон задачи, а глубокое ревью области.
|
||
|
||
Весь процесс с исполнителями — одной схемой:
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
propose["opsx:propose — change, дельта-спеки, tasks.md"]
|
||
checkpoint(["чекпоинт: форму решения одобряет человек"])
|
||
apply["opsx:apply — код, гейт зелёный"]
|
||
|
||
subgraph code["Ревью кода — состав постоянный"]
|
||
cGate["autotests — гейт, источник графа"]
|
||
cS["specs — requirements"]
|
||
cC["code — conventions, техника<br/>и сверка с инвариантами:<br/>security, operations, architecture"]
|
||
cB["basics — только свои темы проекта"]
|
||
cT["triage — единственный сток"]
|
||
end
|
||
|
||
propose --> checkpoint --> apply --> cGate
|
||
|
||
cGate -->|зелёный| cS
|
||
cGate -->|зелёный| cC
|
||
cGate -->|"зелёный, есть свои темы"| cB
|
||
|
||
cS --> cT
|
||
cC --> cT
|
||
cB --> cT
|
||
```
|
||
|
||
**Глубины две, и они не про старательность, а про способ доказательства.**
|
||
**Сверка** — открыть дом темы, открыть дифф, сравнить. **Разбор** — построить
|
||
сценарий рассуждением, ничего не запуская.
|
||
|
||
**Третья глубина — доказательство** (прогнать, померить, построить путь) — в
|
||
цикле задачи не производится вовсе. Она требует машины и стоит часов, и потому
|
||
живёт в скилле `av-dev:code-deep-review`, который идёт по названной области и
|
||
время от времени. Проход, которому в плане назначили доказательство, получил план
|
||
не от конвейера задачи.
|
||
|
||
**Необратимое изменение состава не меняет — оно меняет адресата находки.**
|
||
Миграция схемы и данных, формат на диске, публичный контракт, имя, разошедшееся
|
||
по кодовой базе, — всё, что после мерджа не откатывается обратной правкой. Раньше
|
||
это был отрицательный тест метки `small`: такое изменение поднимало метку и
|
||
получало лишний проход. Поднимать больше нечего, и правило работает иначе:
|
||
находка по необратимому месту помечается `Действие: развилка` и уходит человеку,
|
||
а не чинится молча, каким бы мелким ни был дифф. Цена ошибки тут не в размере
|
||
правки, а в том, что её не отменить.
|
||
|
||
**Состав сверяется до коммита — по таблице тем выше.** Она и есть реестр: тема,
|
||
кто закрывает, против чего. Это единственная защита от промаха, который уже
|
||
случился: пропуск **не отличим от прохода без находок** (гейт зелёный, спеки
|
||
сошлись, отчёт выглядит полным), а заметить его мог бы только триаж, который сам
|
||
заполняется тем, что ему подали. Непущенное идёт строкой «не запускался» с
|
||
причиной, а не отсутствует. Цена молчащего пропуска измерена: семь находок и
|
||
отдельная задача на их дозакрытие.
|
||
|
||
**Сверять теперь дешевле, и это главный выигрыш от снятия метки.** Реестр был
|
||
переменным — он приезжал планом разметки и на каждой задаче выглядел иначе;
|
||
пропущенную тему приходилось искать сверкой двух списков. Реестр постоянный
|
||
сверяется взглядом: против каждой строки таблицы либо отчёт, либо названная
|
||
причина, по которой проход не пущен.
|
||
|
||
## Порядок прогона — граф, а не очередь
|
||
|
||
Таблица тем отвечает «что и против чего проверяется», порядок — «что кого
|
||
ждёт». Ступени остаются единицей **состава**, но порядок задают **не их номера**:
|
||
между ступенями 2 и 3 настоящих зависимостей нет — ни один проход не читает вывод
|
||
другого, — и очередь между ними была бы платой ни за что.
|
||
|
||
Рёбер два вида, и они разной природы. Путать их нельзя: первое про
|
||
**осмысленность** (на красном гейте проходу с мнением не о чем судить), второе
|
||
про **железо**.
|
||
|
||
| Ребро | Смысл | Между кем |
|
||
|---|---|---|
|
||
| **зависимость** | B не стартует, пока A не закончил, потому что без A задание B не определено | гейт → все проходы с мнением; все проходы → триаж |
|
||
| **конфликт за ресурс** | A и B не держат машину одновременно; кто из них первый — неважно, направления у ребра нет | проходы, помеченные «держит машину» |
|
||
|
||
**Узла, который считает состав, у графа нет.** Раньше первым узлом каждого
|
||
прогона стояла разметка и ребро «разметка → все» шло отсюда; состав постоянный, и
|
||
считать его больше нечем и незачем.
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
autotests["autotests<br/>(ступень 1, держит машину)"]
|
||
specs["specs"]
|
||
code["code"]
|
||
basics["basics<br/>(только свои темы проекта)"]
|
||
triage["triage — единственный сток"]
|
||
|
||
autotests -->|зелёный| specs
|
||
autotests -->|зелёный| code
|
||
autotests -->|"зелёный, есть свои темы"| basics
|
||
specs --> triage
|
||
code --> triage
|
||
basics --> triage
|
||
```
|
||
|
||
Читается граф так: **всё, у чего входящие рёбра закрыты, уходит одним
|
||
сообщением**. Источник графа — гейт: он один по построению и идёт первым. После
|
||
зелёного гейта уходят разом `specs` и `code`, а с ними `basics`, если у проекта
|
||
есть свои темы; триаж стартует, когда вернулся последний. Глубина графа — три
|
||
шага при любой задаче, и это же его худший случай.
|
||
|
||
**Схема здесь старше прозы.** Она не иллюстрация к тексту, а сам алгоритм
|
||
планировщика; проза ниже объясняет рёбра и называет их цену. Разошлись — прав
|
||
граф, а расхождение чинится правкой текста.
|
||
|
||
**Ребро значит «A закончил раньше, чем B стартовал», и ничего больше.** В обычном
|
||
графе задач ребро тянет за собой данные — здесь нет, и это не деталь реализации.
|
||
Проход **не видит** находок других проходов, в каком бы порядке их ни запустили.
|
||
Вся ценность конвейера держится на разведённости: под всеми ролями одна модель с
|
||
одними априорными, и стоит показать ей чужой вывод — она согласится. Согласие
|
||
нескольких проходов и так не повышает `confidence` (см. «Честный предел»);
|
||
согласие **наведённое** ещё и маскируется под независимое подтверждение.
|
||
Единственный, кто получает чужие выводы, — триаж, и это его работа.
|
||
|
||
### Кто держит машину
|
||
|
||
Ресурс один и неделимый: **машина** — тесты, поднятый сервис, СУБД, порты, диск.
|
||
Проходы, заявившие его, сериализуются между собой на любой ступени; порядок
|
||
внутри цепочки произволен.
|
||
|
||
| Проход | Держит машину | Почему |
|
||
|---|---|---|
|
||
| `autotests` | да | запускает инструменты проекта — но он источник графа и один по построению |
|
||
| `triage` | да | проверяет оракул `major` запуском — но он сток и тоже один |
|
||
| `specs`, `code`, `basics`, `rubric` | нет | читают и рассуждают, ничего не исполняют |
|
||
|
||
**В цикле задачи цепочки за машину нет.** Оба прохода, что её держали —
|
||
`adversary` и `ops`, — переехали в скилл `av-dev:code-deep-review`; там правило
|
||
действует целиком, и дом его остаётся здесь. Оставшиеся двое машину держат, но
|
||
каждый один по построению: один источник графа, другой сток.
|
||
|
||
**Правило про ресурс, а не про имена.** Раньше здесь стояло именованное
|
||
исключение «`adversary` и `ops`»; оно рассыпается, как только проход начнёт
|
||
мерить или в проекте появится свой. Два прохода на одной машине соревнуются за
|
||
диск, CPU и за саму СУБД и выдают числа, которые не воспроизведутся, — а число,
|
||
снятое под конкурентную нагрузку, это находка с испорченным оракулом. Её
|
||
опровержение стоит дороже всего выигрыша от параллельности, и она хуже
|
||
отсутствующей: выглядит доказанной. Правило выведено из находок, целиком
|
||
державшихся на таких замерах; у каждого проекта они свои и лежат в журнале
|
||
`docs/review.md`.
|
||
|
||
Проект вправе пометить «держит машину» и другой проход — в `docs/review.md`,
|
||
разделе настройки конвейера. Снимать пометку с перечисленных нельзя.
|
||
|
||
### Находка «переделать форму» — прогон повторяется целиком
|
||
|
||
**Барьера стоимости в конвейере нет, и раннего выхода тоже.** Барьер существовал
|
||
ради независимой реализации — единственного прохода, чей счёт определялся объёмом
|
||
вывода, — и ушёл вместе с ней. Граф плоский, от гейта до триажа: защищать за
|
||
барьером нечего, а сериализация не бесплатна — она разводит по очереди то, что
|
||
могло идти разом.
|
||
|
||
Находка «**форму изменения** надо переделывать» ловится триажем, как и любая
|
||
другая; дальше правило одно. Находка чинится, и ревью кода запускается **заново с
|
||
гейта**, а не «доезжает» остатком по коду, которого через час не станет.
|
||
|
||
**Пересчитывать перед повтором нечего.** Состав постоянный, и второй прогон
|
||
идёт тем же составом, что первый; менять его нельзя даже «раз уж переделываем» —
|
||
конвейер, чей состав зависит от истории прогонов, не сверяется ни с чем.
|
||
Если прогон всё же остановлен на полпути, незапущенные проходы идут в границы
|
||
покрытия строкой «не запускался: прогон остановлен на <проход> из-за <находка>»,
|
||
поимённо, а **триаж на половине прогона не запускается**: его отчёт выглядит
|
||
полным, потому что агрегирует всё, что ему подали, — это тот же молчащий пропуск,
|
||
что и в разделе «Состав прогона».
|
||
|
||
Находка, которая чинится в пределах существующей формы (`Действие: инлайн`),
|
||
прогон не останавливает: дешевле дособрать все находки и починить пачкой, чем
|
||
гонять конвейер дважды.
|
||
|
||
### Линеаризация — когда графа мало
|
||
|
||
Граф можно вытянуть в одну цепочку. Это отступление, и оно называется в отчёте:
|
||
|
||
1. **сказал оператор** — «гони линейно». Набора называть не надо: линейный прогон
|
||
ничего не портит, он только дольше, и домысливать тут нечего;
|
||
2. **машина занята, и знает об этом вызывающий.** Рядом идёт другая задача,
|
||
поднят сервис, гоняется дорогая проверка проекта. Сам конвейер занятости
|
||
машины не видит — её обязан назвать тот, кто запускает;
|
||
3. **разбор самого конвейера** — когда выясняется, почему проход чего-то не
|
||
нашёл, порядок и изоляция важнее скорости.
|
||
|
||
Обратное отступление — **слить цепочку ресурса** (пустить меряющие проходы
|
||
разом) — бывает только по прямому слову оператора, и тогда в границы покрытия
|
||
идёт строка: какие проходы шли одновременно и что замеры этого прогона как
|
||
оракул слабее.
|
||
|
||
Режим объявляется в отчёте отдельной строкой: **`по графу`** — одним словом,
|
||
**`линейно`** — с причиной (какой именно из трёх).
|
||
|
||
## Прогон без change — сценарий обслуживания
|
||
|
||
Третий вызывающий конвейера — сценарий обслуживания скилла `av-dev:code-resolve`
|
||
(тулчейн и сборка, зависимости, гит-хуки, перенос, чистка). Он приходит **без
|
||
change**: у работы, не меняющей поведения, дельта-спек нет по построению.
|
||
|
||
**Копия.** Дом оси — `shared/axes.md` в репозитории плагина: режим делят конвейер,
|
||
сценарий обслуживания и два устава, и ни один из них им не владеет. Правится дом,
|
||
а не этот файл.
|
||
|
||
<!-- копия: режим-прогона из av-dev/shared/axes.md -->
|
||
|
||
**Прогон ревью идёт в одном из двух режимов, и режим — не глубина.**
|
||
|
||
- **По change** — обычный прогон цикла задачи: есть дифф и дельта-спеки, состав
|
||
постоянный и живёт в конвейере.
|
||
- **Без change** — дельта-спек нет по построению, и вместе с ними нет темы
|
||
`requirements`. План фиксирован и назван вызывающим; так идёт сценарий
|
||
обслуживания.
|
||
|
||
**Режим правит состав, а не глубину.** Глубина темы стоит в таблице тем
|
||
конвейера, одна на все прогоны по change; на прогоне без change её называет план
|
||
сценария — иначе проход, чьей темы в плане нет, взял бы глубину наугад.
|
||
|
||
**Третьего режима у конвейера нет.** Скилл `av-dev:code-deep-review` конвейер не
|
||
зовёт вовсе: состав, глубина и вход у него свои, а общее с конвейером — уставы
|
||
проходов и контракт находок.
|
||
|
||
<!-- /копия: режим-прогона -->
|
||
|
||
**План приходит вызовом и фиксирован сценарием**, а не выводится здесь. **Он же
|
||
называет темы и глубину каждого прохода** — таблица тем конвейера описывает
|
||
прогон по change, и тема `requirements` в ней есть, а здесь её предмета нет:
|
||
|
||
<!-- копия: план-обслуживания из av-dev/skills/code-resolve/references/maintain.md -->
|
||
|
||
| Тема | Дом | Кто закрывает | Глубина и вход | Когда |
|
||
| --- | --- | --- | --- | --- |
|
||
| `autotests` | `CLAUDE.md`, семантика гейта | `review-autotests` | как обычно: гейт запускается целиком | всегда |
|
||
| `operations` | `architecture.*`, раздел эксплуатации | `review-basics` | **сверка**: дом темы против диффа, потолок 2 | всегда |
|
||
| `conventions` + технический разбор | `conventions.*` | `review-code` | как в цикле: дом конвенций целиком, потолок 4 конвенционных, у техники потолка нет; сверка с инвариантами `CLAUDE.md` по темам `security`, `operations`, `architecture`, потолок 1 | дифф трогает код, а не только оснастку |
|
||
|
||
<!-- /копия: план-обслуживания -->
|
||
|
||
Триаж обязателен и здесь — он единственный сток и единственный, кто сверяет план
|
||
с исходом; на его вход подаётся этот план вместо таблицы тем. Тема
|
||
`requirements` в плане отсутствует за отсутствием предмета; `security` и
|
||
`architecture` закрыты сверкой с записанными инвариантами внутри `code` — ровно
|
||
так же, как в цикле задачи. Все три обязаны быть названы в границах покрытия.
|
||
|
||
Дом плана — сценарий, а не этот скилл: `av-dev:code-resolve`,
|
||
`references/maintain.md`, раздел «Ревью — план фиксирован сценарием».
|
||
|
||
**Правило гейта на таком прогоне работает жёстче обычного.** Правка, которая
|
||
трогает сам гейт, проверяется гейтом же — инструмент проверяет себя, — поэтому
|
||
сверяется не только цвет, но и состав шагов. Что считается составом, объявляет
|
||
проект семантикой гейта в `CLAUDE.md`; не объявил — это строка границ покрытия, а
|
||
не догадка прохода.
|
||
|
||
## Ступень 1 — Автотесты (обязательна)
|
||
|
||
Агент `review-autotests`, тема `autotests`. Гонит команду гейта из семантики
|
||
гейта в `CLAUDE.md` — либо засчитывает прогон, сделанный до ревью, — и
|
||
интерпретирует вывод.
|
||
|
||
**Тема и проход названы одинаково намеренно, а «гейт» осталось именем команды.**
|
||
Раньше тема звалась `autotests`, а проход — `gate`: одна сущность под двумя
|
||
именами, и вопрос проекта, адресованный одному имени, к другому не приезжал.
|
||
Слово «гейт» теперь значит ровно одно — барьер, который проект запускает; тема
|
||
шире него ровно на «чего в гейте намеренно нет».
|
||
|
||
**Гейт, прогнанный до ревью, второй раз не гоняется.** Задача приходит на ревью
|
||
с зелёным гейтом: сценарий решения доводит его до зелёного шагом `opsx:apply`,
|
||
сценарий обслуживания — своим шагом гейта. Повтор на неизменившемся дереве
|
||
вернёт тот же вывод, а стоит он минут — то есть платит ими ни за что.
|
||
|
||
**Признак один и проверяемый — отпечаток рабочего дерева.** Его снимают дважды:
|
||
тот, кто прогнал гейт, сразу после прогона, и проход перед началом работы.
|
||
|
||
<!-- дом: отпечаток-дерева -->
|
||
|
||
```sh
|
||
{ git rev-parse HEAD; git status --porcelain -uall; git diff HEAD;
|
||
git ls-files -o --exclude-standard -z | xargs -0 -r git hash-object; } | sha1sum
|
||
```
|
||
|
||
<!-- /дом: отпечаток-дерева -->
|
||
|
||
Сводку прошлого прогона, путь к логам шагов и отпечаток проход получает
|
||
**заданием** — их передаёт вызывающий скилл. Отпечатки совпали — проход читает
|
||
готовую сводку и логи, команду не запускает. Разошлись, отпечатка в задании нет,
|
||
логи недоступны — проход гонит гейт сам и ни у кого не спрашивает.
|
||
|
||
**Отказ здесь безопасен по построению.** Лишний прогон стоит минут, а
|
||
засчитанный чужой — красноты, которой никто не увидел. Временный каталог проекта
|
||
из отпечатка выпадает сам: `--exclude-standard` отбрасывает игнорируемое, а логи
|
||
шагов гейт пишет именно туда. У проекта, держащего временный каталог под git,
|
||
отпечатки не совпадут никогда — и он получит честный прогон вместо тихого
|
||
засчитывания.
|
||
|
||
**Переиспользуется команда, а не проход.** Тема `autotests` закрывается целиком:
|
||
логи проход читает сам, находки об отсутствующей верификации выдаёт как обычно.
|
||
Переиспользование он объявляет строкой сводки и строкой границ покрытия — чем
|
||
гейт прогнан, когда и на каком отпечатке. Молчащее переиспользование неотличимо
|
||
от собственного прогона, а разница между ними в том, кто видел вывод своими
|
||
глазами.
|
||
|
||
**Пока гейт красный — проходы с мнением не запускаются.** Оркестратор чинит и
|
||
перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки
|
||
(гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не
|
||
блокирует.
|
||
|
||
Гейт возвращает не только «зелено/красно», но и находки класса **отсутствующая
|
||
верификация**: изменённые строки без покрытия, конкурентность без теста с
|
||
параллельным доступом, флаки-тест (не ниже `major`), недоступный инструмент.
|
||
|
||
Шаги выбираются по изменённым файлам: правка документации не гоняет тесты,
|
||
линтеры и детектор гонок. Пропуск при этом не молчит — он виден в сводке с
|
||
причиной и уезжает в границы покрытия, как и любой другой `SKIP`.
|
||
|
||
Шаги, которые красят гейт безусловно, перечислены в `CLAUDE.md` с причиной. Проходу
|
||
запрещено списывать такой отказ в мелочь.
|
||
|
||
## Ступень 2 — Сверка (обязательна)
|
||
|
||
Два прохода, оба против **записанного** критерия. Машину не держат ни один, ребра
|
||
между ними нет — уходят одним сообщением сразу после зелёного гейта, вместе со
|
||
ступенью 3, если она идёт.
|
||
|
||
- `review-specs` закрывает тему `requirements`. Критерий взят из **дельта-спек
|
||
предлагаемого изменения**, а не из proposal, сообщения коммита или описания
|
||
задачи. Сверка двунаправленная; направление `code → spec` важнее.
|
||
- `review-code` закрывает тему `conventions` **и делает технический разбор
|
||
кода** — это две его половины. Первая ищет дефект, который сработает сам, на
|
||
обычном входе: необработанная ветка отказа, пустое значение, граница диапазона,
|
||
перепутанный операнд, неосвобождённый ресурс, неверно применённый интерфейс
|
||
библиотеки. Вторая сверяет с конвенциями проекта, беря только ту их часть,
|
||
которая **не выражается правилом**: механизируемое уже проверила ступень 1.
|
||
**Третья его обязанность узкая и постоянная** — сверить дифф с записанными
|
||
инвариантами `CLAUDE.md` по темам `security`, `operations` и `architecture`.
|
||
Потолок 1 находка на все три темы разом: это не разбор темы, а объявленный
|
||
минимум, и в границах покрытия он называется именно так.
|
||
|
||
**Вход обоих постоянный и полный:** `specs` читает дельта-спеки и затронутые
|
||
актуальные спеки, `code` — дом конвенций целиком, до чтения диффа. Прежде вход
|
||
сужала метка `small` до дельта-спеки и индекса конвенций; узкий вход ловит
|
||
нарушение записанного рода и пропускает то, ради чего конвенцию писали абзацем,
|
||
— то есть экономил ровно на той работе, ради которой проход и зовут.
|
||
|
||
**Потолки у половин `code` раздельные, и это не бюрократия.** Конвенционных
|
||
находок больше по построению — родов навигации в разы больше, чем классов
|
||
технического дефекта, — и в общем списке они вытесняют техническую половину, чей
|
||
пропуск дороже. Раздельный потолок делает вытеснение невозможным: конвенционных
|
||
4, инвариантных 1, у технической половины потолка нет.
|
||
|
||
**Технический разбор — не тема, а обязанность прохода, и он единственный.**
|
||
Остальные читают код как материал для своей оптики: `specs` — против требований,
|
||
`basics` — против отказов окружения, `architecture` — против устройства. «Здесь
|
||
ошибка в логике» не говорит больше никто, и до недавнего времени не говорил
|
||
никто вовсе: `code` был проходом только по конвенциям, а дефект ловился разве что
|
||
случайно. Это была самая крупная дыра конвейера, и стоила она дороже любой
|
||
недосмотренной темы.
|
||
|
||
Recall темы `conventions` равен длине конвенций проекта — это предел любой
|
||
сверки, и снимает его не цикл задачи, а глубокое ревью области.
|
||
|
||
**Оба прохода на верхней модели, и по одной причине — цене пропуска.** У `specs`
|
||
это направление `code → spec`: надо заметить **отсутствие** — тихий фолбэк,
|
||
самодеятельный дефолт, проглоченную ошибку. У `code` это пропущенный дефект,
|
||
который поедет в прод. Ни то ни другое не оставляет следа ни в отчёте, ни в
|
||
границах покрытия; прочие проходы с мнением держат `opus` из-за цены **ложных**
|
||
находок, эти двое — из-за цены пропущенных.
|
||
|
||
**Эта ступень и есть цикл задачи.** С уходом тяжёлых проходов на ней держится всё,
|
||
что прогон вообще проверяет по существу: заказанное против сделанного, дефект,
|
||
который сработает сам, и конвенции проекта. Отсюда и решение не ставить потолка
|
||
технической половине.
|
||
|
||
## Ступень 3 — Темы проекта (только когда они есть)
|
||
|
||
Агент `review-basics`. Один проход, машину не держит, ничего не запускает и не
|
||
меряет — уходит одним сообщением вместе со ступенью 2, сразу после зелёного
|
||
гейта.
|
||
|
||
**Он приёмник проектных тем, и больше ничей.** Именных проходов конечное число, а
|
||
тем столько, сколько заведёт проект: без приёмника открытость списка тем была бы
|
||
обещанием без механизма. Темы **ядра** он больше не держит — риск и устройство
|
||
закрывает `code` сверкой с инвариантами, а разбор этих тем целиком уехал в
|
||
`av-dev:code-deep-review`.
|
||
|
||
**Запускается тогда и только тогда, когда ему есть что принимать.** Своих тем у
|
||
проекта нет — проход не идёт вовсе, и отчёт говорит об этом строкой: «свои темы
|
||
проекта не заведены, приёмник не запускался». Это единственное место, где состав
|
||
прогона зависит от проекта, и потому оно называется явно.
|
||
|
||
Глубина одна — **разбор**: построить сценарий рассуждением, дом темы против
|
||
диффа; потолок 4 находки. Прежде глубина приезжала планом разметки и на `small`
|
||
падала до сверки; плана нет, и падать ей больше неоткуда.
|
||
|
||
Чего он не делает ни на какой теме — замеров, эксперимента против драйвера,
|
||
построенного пути, карты проекта, границы домена. Всё это стоит машины или входа
|
||
шире диффа, то есть глубокого ревью области.
|
||
|
||
## Ступень 4 — Triage (обязательна)
|
||
|
||
Агент `review-triage`. **Единственный сток графа и единственный, кто агрегирует.**
|
||
Входящие рёбра — все запущенные проходы: пока хоть один не вернул отчёт, триаж не
|
||
стартует. Получает сырые выводы всех проходов, `git diff`, режим и **таблицу
|
||
тем**; возвращает финальный отчёт.
|
||
|
||
**На прогоне без change её место занимает план сценария** — см. «Прогон без
|
||
change»: сверять исход с планом триаж обязан и там, а другого перечня тем в том
|
||
прогоне не существует.
|
||
|
||
**Таблица тем на входе у триажа — не формальность, а сверка.** Он единственный,
|
||
кто видит и то, что заявлено, и то, что пришло: «тем шесть, отчёты покрывают
|
||
пять» — находка о самом прогоне, и заметить её больше некому. Раньше он получал
|
||
список запущенных проходов и потому мог сверить только состав; теперь сверяет
|
||
**темы**, а тема, оставшаяся без отчёта, — это то, чего список проходов никогда
|
||
не показывал. Таблица постоянная, и сверка потому дешевле прежней: сравнивать
|
||
приходится с одним и тем же реестром, а не с планом, который на каждой задаче
|
||
выглядел иначе.
|
||
|
||
Отсюда же правило, которое иначе выглядит придиркой: **триаж на неполном графе не
|
||
запускается**. Прогон, остановленный на полпути находкой «переделать форму», до
|
||
стока не доезжает — его отчёт агрегировал бы половину и выглядел бы полным.
|
||
|
||
Без триажа проходы дают порядка сорока замечаний при единицах существенных.
|
||
Потребитель здесь — оркестратор, который **молча реализует** всё, что прочитал:
|
||
цена нетриажированного отчёта — не потерянное время человека, а разросшийся от
|
||
вкусовщины код.
|
||
|
||
Порядок: дедупликация по причине → оракул для всего `critical`/`major` →
|
||
понижение неподтверждённого до гипотезы → отсев вкусовщины → ранжирование по
|
||
ущербу × вероятности → потолок 7 пунктов в основном списке.
|
||
|
||
**Разметку действия ставит он же, и умолчание у неё одно — `инлайн`.** Развилку
|
||
получает только то, что инлайном чинить нельзя: находка по необратимому месту и
|
||
находка, чья правка меняет дельта-спеки. Остальное чинится молча — см. «Что
|
||
происходит с находками дальше».
|
||
|
||
**Он же собирает строки «отложено в `av-dev:code-deep-review`».** Проход, упёршийся
|
||
в предел цикла — нужен замер, нужен прогнанный путь, нужен вход шире диффа, —
|
||
пишет об этом в своих границах покрытия; триаж сводит такие строки в одну секцию
|
||
отчёта. Без сведения они растворяются по отчётам проходов, и повод позвать
|
||
глубокое ревью не накапливается нигде.
|
||
|
||
## Контракт находок
|
||
|
||
Единый для всех проходов — [references/finding-contract.md](references/finding-contract.md).
|
||
Коротко: заголовок через **последствие**, обязательные поля `Файл`, `Severity`,
|
||
`Confidence`, `Оракул`, `Последствие`, `Предложение`, `Найдено проходом`.
|
||
`critical` без оракула или построенного пути не существует. Находка без поля
|
||
«Последствие» не выводится вовсе.
|
||
|
||
Каждый проход завершает вывод блоком `## Coverage of this pass`.
|
||
|
||
## Что происходит с находками дальше
|
||
|
||
**Умолчание одно, и оно называется прямо: находку чинит агент, молча.** Цикл
|
||
задачи устроен так, чтобы человек читал сводку, а не разбирал список замечаний;
|
||
всё, что чинится в пределах одобренной формы решения, помечается `Действие:
|
||
инлайн`, уходит агенту дословно вместе с оракулом и логированию не подлежит.
|
||
Прогон, вернувший человеку десяток вопросов, свою работу не сделал.
|
||
|
||
Из умолчания два выхода, и оба узкие:
|
||
|
||
- **`Действие: развилка`** — вопросом с вариантами и ценой каждого туда, где
|
||
проект держит вопросы (это знает вызвавший скилл, а не конвейер ревью).
|
||
Помечается так **только** то, что инлайном чинить нельзя: находка по
|
||
необратимому месту (миграция, формат на диске, публичный контракт) и находка,
|
||
чья правка меняет **дельта-спеки** — то есть отменяет одобренное человеком.
|
||
Оркестратор при этом не останавливается: он урезает изменение до остатка и
|
||
доводит его.
|
||
- **урожай** — находка реальная, но не для этого мерджа: отложенный `major`,
|
||
развилка, решённая «потом», пачка `nit`. Конвейер отдаёт её **списком** в
|
||
отчёте: формулировка, оракул, откуда взялась (какой проход, какой change).
|
||
|
||
**Задачи из урожая заводятся только по слову человека, и это правило, а не
|
||
вежливость.** Спрашивает не конвейер: список уезжает вызывающему и показывается
|
||
человеку **одной репликой на весь хвост задачи** — вместе с тем новым, что
|
||
предлагает записать синк документации (`av-dev:code-resolve`,
|
||
`references/solve.md`, шаг 6). Два вопроса про одно и то же — «что из найденного
|
||
переживёт задачу» — стоили бы двух переключений вместо одного. Сказал «заводим» —
|
||
зовётся `av-dev:task-track`, у него на этот вход отдельный сценарий «задачи из ревью и
|
||
аудита»: свой формат, кластеризация по причине, дедуп против беклога и кладбища.
|
||
Не сказал — урожай остаётся строками доклада, и это исход, а не потеря. Прогон,
|
||
заводящий задачи сам, наполняет беклог работой, которую никто не выбирал; на
|
||
проекте, где очередь работ ведёт один человек, это и есть главная цена лишней
|
||
находки. Каталога задач в проекте нет — урожай остаётся списком тем более, и
|
||
это говорится строкой.
|
||
|
||
Остальное не меняется:
|
||
|
||
- `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/`. Кто ищет отчёт после архивации
|
||
(приёмщик на груминге `av-dev:task-groom`, разбор дефекта), смотрит **оба**
|
||
пути; «отчёта нет» объявляется, только когда пуст и архивный, иначе самый
|
||
дорогой сценарий «состав ревью неизвестен, гоняем заново» срабатывает на
|
||
каждой доведённой задаче.
|
||
|
||
## Честный предел
|
||
|
||
Модель воспроизводит медиану публичного кода, смещённую к популярному и
|
||
туториальному: отсюда тяга к интерфейсам ради интерфейсов, лишним мокам и
|
||
конфигурируемости, которую никто не просил. **«Идиоматично» и «распространено» —
|
||
разные вещи**; проходы обязаны различать их и опираться на поимённое положение
|
||
руководства, а не на ощущение частотности.
|
||
|
||
Согласие нескольких проходов — **не подтверждение**: это один источник,
|
||
высказавшийся несколько раз. Совпадение повышает приоритет, но не `confidence`.
|
||
|
||
Что недоступно **этому** проекту принципиально — перечисляет «Недоступно
|
||
проверке» в `docs/review.*`, по темам, и оба его подраздела целиком уезжают в
|
||
границы покрытия. **Тема, у которой нет дома, — тоже граница покрытия**, и она
|
||
объявляется на каждом прогоне, а не разово.
|
||
Независимо от проекта недоступно:
|
||
|
||
- поведение внешних систем в их будущих версиях;
|
||
- реальный профиль нагрузки и то, что на самом деле лежит в данных;
|
||
- завязка внешних потребителей на текущую форму ответа;
|
||
- суждение «этой функциональности не должно существовать».
|
||
|
||
Отдельно и честно: **поимённая сверка с положениями руководств по стилю языка не
|
||
задаётся ни одним проходом.** Проход про идиоматичность упразднён, его способные
|
||
части переселены (эксперимент против поведения библиотеки и драйвера — в `ops`,
|
||
«не изобретаем ли то, что уже есть в библиотеке» — в `architecture`, и оба теперь
|
||
живут в `av-dev:code-deep-review`), но различение «идиоматично против
|
||
распространено» не спрашивает никто. Класс обратимый — портит форму кода, не
|
||
данные, — и его надо признавать в границах покрытия, а не считать проверенным.
|
||
|
||
**Форму решения в цикле не судит никто, и это сознательное сужение.** Ни ревью
|
||
дизайна до кода, ни архитектурного прохода после — обоих сняли, и оба ушли по
|
||
одной причине: суждение о форме стоит разговора с человеком, а разговор внутри
|
||
задачи растягивает её в часы. Форму одобряет **человек на чекпоинте**, до кода, и
|
||
это единственное место цикла, где решение о ней принимается. Всё, что видно
|
||
только по написанному коду — второй способ делать уже делаемое, лишний слой,
|
||
интерфейс ради мока, — ловится глубоким ревью области, то есть позже и не всегда.
|
||
Класс идёт строкой в границы покрытия каждого прогона.
|
||
|
||
**Запуском в цикле не проверяется ничего сверх гейта, и это на всякой задаче.**
|
||
Формулировка «не запускается ничего» была бы короче и была бы ложью: гейт
|
||
запускает инструменты проекта, а триаж проверяет оракул `critical`/`major`
|
||
запуском — оба идут всегда. Не проверяется **проходом с мнением**: построенный
|
||
путь атаки (его надо прогнать), поведение библиотеки и драйвера в вырожденном
|
||
случае (достаётся только экспериментом), любое число — время удержания
|
||
блокировки, пик кучи, темп роста журнала, стоимость на годовой истории.
|
||
|
||
**Три темы риска и устройства смотрятся только против записанных инвариантов.**
|
||
Отдельная строка, и она обязательна в каждом отчёте: `security`, `operations` и
|
||
`architecture` закрывает `code` сверкой с `CLAUDE.md`, потолком 1 находка на все
|
||
три. Свойства, которого нет в инвариантах, в цикле не спросит никто. Это не
|
||
«глубина ниже» — это **другой дом темы**, куда более узкий, и путать одно с
|
||
другим нельзя.
|
||
|
||
**Ось времени в цикле не смотрит никто.** Обратима ли миграция, что станет с
|
||
записями новой версии после отката, как узел ведёт себя через неделю роста —
|
||
раньше эти вопросы задавал приёмник тем на метке `medium`, теперь метки нет, а
|
||
приёмник держит только свои темы проекта. Взамен работает адресация: находка по
|
||
необратимому месту идёт человеку развилкой, а не чинится молча. **Это не
|
||
равноценная замена, и подменять одно другим нельзя:** развилка срабатывает,
|
||
только если находку кто-то сделал, а по оси времени в цикле её теперь делает
|
||
разве что инвариант.
|
||
|
||
**Решения и измеренные числа проекта прогон не читает вовсе.** `adr.*` и
|
||
`research.*` — процессные документы. Отсюда две строки в границы покрытия каждого
|
||
прогона: расхождение изменения с записанным решением ловится не здесь, а сверкой
|
||
документации; число, на которое опирается находка, обязано быть снято **на этом
|
||
прогоне**, иначе находка не поднимается выше гипотезы. Раньше числа брались из
|
||
`docs/research/`, и находка выглядела доказанной чужим замером неизвестной
|
||
свежести.
|
||
|
||
**Темы при этом названы все — но закрыты они по-разному, и это надо читать
|
||
буквально.** «Тема `security`, глубина сверка» не значит «безопасность
|
||
проверена»: значит, что дом темы открыли, дифф посмотрели и сравнили.
|
||
|
||
**Доказательства в цикле задачи нет, и это самая крупная его граница.** Класс
|
||
дефектов, который виден только построенным путём и снятым числом — гонка,
|
||
деградация под нагрузкой, исчерпание ресурса, откат бинаря поверх новой схемы, —
|
||
здесь не ловится ничем.
|
||
|
||
Это сознательная сделка, а не пробел в устройстве: тяжёлые проходы оплачивались
|
||
на каждой задаче, где запускались, а получались на немногих. Теперь они живут в
|
||
`av-dev:code-deep-review` и оплачиваются тогда, когда их решают получить.
|
||
Проверяется сделка не рассуждением, а двумя следами: **строками «отложено»** в
|
||
отчётах — если по одному месту повторяется один и тот же неснятый замер, глубокий
|
||
прогон просрочен, — и **журналом дефектов**: класс, всплывающий после мерджа,
|
||
значит, что прогон надо звать чаще.
|
||
|
||
Так же честно и про упразднённый проход: **«не знаю, чего не знаю» больше
|
||
не достаёт никто.** Проход независимой реализации писал свою версию узла, не
|
||
открывая существующую, и диффил по решениям — декомпозиция, владение данными,
|
||
модель конкурентности, форма решения там, где спека выбора не сделала. Он снят по
|
||
решению оператора о **стоимости** — счёт определялся объёмом вывода, и на прогон
|
||
он тратил больше всех остальных проходов вместе, — а не по замеру, который
|
||
[calibration.md](references/calibration.md) требует перед удалением. Значит и
|
||
записывается это как сознательное сужение, а не как «класс оказался пустым».
|
||
Класс идёт строкой в границы покрытия каждого прогона — там же, где проект
|
||
перечисляет своё в подразделе «перестали проверять сознательно».
|
||
|
||
Это и есть причина, по которой конвейер готовит ревью, а не заменяет его.
|
||
|
||
## Ссылки
|
||
|
||
- [references/project-facts.md](references/project-facts.md) — что нужно проходу
|
||
и где это лежит в документах проекта; таблица поразрядной деградации.
|
||
- Skill `av-dev:code-deep-review` — глубокое ревью области кода: там живут
|
||
`review-adversary`, `review-ops` и `review-architecture`, там же единственное
|
||
место процесса, где находка доказывается прогоном и замером, а форма решения
|
||
вообще обсуждается.
|
||
- Skill `av-dev: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) — журнал проскочивших дефектов.
|