Files
dev-skills/av-dev-pipeline/skills/review-pipeline/SKILL.md
T
av 0eca206460 av-dev-pipeline: починены находки ревью, бриф заводится скиллом
- скилл project-brief: бриф собирается из CLAUDE.md, архитектуры, Taskfile
  и конвенций и показывается человеку. Раньше единственная инструкция по
  его созданию лежала внутри шаблона, поэтому деградированный режим был не
  аварийным, а единственным: critical по основанию «нарушен инвариант»
  недостижим ни на одной задаче
- rebase перенесён внутрь worktree задачи: прежняя форма падала на занятой
  ветке, и агент уводил весь батч в провалившиеся с ложной причиной
- контракт брифа дополнен восемью слотами; проверен заполнением на обоих
  проектах, незаполнимых нет. Прецедент healthlog вынут из общего charter'а
  в бриф — там он вмёрз вместе с числами
- шов: пайплайн задачу не закрывает и записи учёта не трогает, урожай
  отдаёт списком, правило остатка — ссылкой на av-dev-tasks
- деградированный абзац во всех девяти проходах, вопрос 9 в ops,
  пространство имён в вызовах, раздел предпосылок
2026-08-03 11:45:40 +03:00

452 lines
40 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: review-pipeline
description: Конвейер ревью изменения — детерминированный гейт, сверка с дельта-спеками в обе стороны, враждебные постановки и эксплуатационный постмортем, независимая реализация по триггеру, архитектура и обязательный триаж. Проходы гонятся последовательно; параллельно — только по явной просьбе и с явно названным набором. Проектная специфика приходит из файла-брифа. Вызывается из task-pipeline (чекпоинты ревью), из task-batch (финальная сверка) и отдельно — профилем design на предложении ДО кода.
---
# Конвейер ревью
Готовит ревью — **не заменяет его**. Потребитель отчёта — оркестратор, который
чинит код; человек читает только сводку, развилки и границы покрытия.
## Три правила, из которых всё следует
Если ситуация не покрыта инструкцией — решай по ним.
1. **Recall чек-листа равен длине чек-листа.** Проход, устроенный как «проверь
пункты 1..N», найдёт ровно перечисленное. Всё неявное — идиомы, форма
решения, «так не делают» — неперечислимо по определению: перечислимое уже
стало бы конвенцией. Отсюда деление проходов на **applicative** (применяют
заданный критерий) и **generative** (сперва порождают критерий или
альтернативу, потом сравнивают). Расширять чек-листы бесполезно; неявный слой
достают только generative-проходы.
2. **Ценность верификатора = наличие внешнего оракула × декорреляция с
автором**, а не число ролей. Под всеми ролями одна модель с одними
априорными, вход у всех общий: седьмая роль почти не добавляет recall, но
линейно удорожает триаж. Иерархия надёжности: детерминированный инструмент >
агент, который его **запускает** и интерпретирует вывод > агент с чистым
мнением. Максимум работы переносим вниз.
3. **Отчёт без границ покрытия хуже отсутствия отчёта.** «Критичных проблем не
обнаружено» потребляет ощущение проверенности, ничего не гарантируя. Секция
границ покрытия обязательна и не сокращается — в том числе в докладе человеку.
## Предпосылки
Конвейер опирается на внешнюю обвязку и без неё работает не целиком. Проверь это
один раз, при установке плагина в проект:
- **OpenSpec и скиллы `opsx:*`.** Профиль `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, либо сознательно не зовёт
`review-specs` и профиль `design` — и тогда это идёт строкой «не запускался» в
границы покрытия, как любой другой пропуск.
- **Бриф проекта** — см. следующий раздел. Заводится скиллом, а не руками.
- **Проектные копии этих скиллов и агентов удаляются при установке.** Если в
проекте уже лежат свои `.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-pipeline:project-brief`.
## Что конвейер защищает — приходит из брифа
Проходы общие, а нарушать нельзя проектное. Список инвариантов, команду гейта,
объёмы, прецеденты и модель угроз конвейер **не знает** — он читает их в брифе
проекта: [references/project-brief.md](references/project-brief.md) описывает
контракт, [references/brief-template.md](references/brief-template.md) — образец
заполнения.
Разреши путь к брифу один раз, в начале прогона: путь из задания →
`docs/review-brief.md``.claude/review-brief.md`. Дальше передавай готовым.
**Брифа нет по всем трём путям — заведи его, а не понижай прогон.** Вызови Skill
**`av-dev-pipeline:project-brief`**: он соберёт бриф из `CLAUDE.md`, архитектуры,
файла задач и конвенций, покажет человеку и вернёт путь. Это механика, а не
развилка: спрашивать разрешения не нужно, и остановка прогона тут не
предусмотрена. Заведение стоит одного шага один раз на проект — деградированный
режим платит на каждой задаче.
**Деградированный режим — исход, а не умолчание.** Он включается ровно тогда,
когда бриф завести не удалось (репозиторий на чтение, человек прямо запретил,
инварианты вывести неоткуда): `critical` по основанию «нарушен инвариант
проекта» никем не присваивается, числа объёма не используются, и в границы
покрытия уезжает строка «брифа проекта нет, завести не удалось: <причина>».
Причина обязательна — без неё строка неотличима от «мы просто не стали».
## Что получает каждый проход
Задание любому проходу состоит из шести вещей, и первая — главная: без брифа
проход теряет предмет проверки и уходит в деградированный режим.
- **бриф** — путь (разрешён или заведён на старте, см. выше);
- **контракт находок** — путь к
[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 | 78 |
| `design` | **до кода**, на предложении | specs + rubric + architecture (см. ниже) | 3 |
**Состав сверяется по этой таблице до коммита.** Реестр из трёх-восьми пунктов
проверяется взглядом — и это единственная защита от промаха, который уже
случился: пропуск прохода **не отличим от прохода без находок** (гейт зелёный,
спеки сошлись, отчёт выглядит полным), а заметить его мог бы только триаж,
который сам заполняется тем, что ему подали. Отчёт обязан перечислять запущенные
проходы **поимённо и с исходом**; непущенный идёт строкой «не запускался» в
границы покрытия, а не отсутствует. Цена молчащего пропуска измерена: семь
находок и отдельная задача на их дозакрытие.
Правило выбора профиля — **по факту изменения, не по ощущению важности**:
- есть миграция схемы, новый пакет, изменение публичного контракта (API,
протокол, формат на диске) или трогается правило, определяющее идентичность и
слияние данных → `deep`;
- иначе меняется поведение, видимое снаружи (эндпоинт, форма ответа, код ответа,
формат лога) → `standard`;
- иначе → `quick`.
Что именно в этом проекте считается публичным контрактом и какие пути означают
`deep` — раздел `## Триггеры` брифа. Он **уточняет** правило, а не отменяет его:
если триггеров в брифе нет, работает список выше.
Профиль объявляется в отчёте. Понижение профиля — решение оркестратора, и оно
попадает в границы покрытия строкой «профиль понижен до X, потому что …».
## Режим запуска: параллельно или последовательно
Профиль отвечает «какие проходы», режим — «как их запускать». Стадии всегда идут
по порядку номеров; выбор касается только проходов **внутри** стадии.
| Режим | Как | Когда |
|---|---|---|
| **последовательно** (умолчание) | по одному, следующий стартует после отчёта предыдущего | всегда, пока не попросили иначе |
| **параллельно** | названные проходы — одним сообщением | только по явной просьбе **и** с явно названным набором |
**Умолчание — последовательно, и его не надо обосновывать.** Обосновывается
отступление.
**Параллельный режим включается при двух условиях сразу**, и второе так же
обязательно, как первое:
1. **о нём попросили явно** — «гони параллельно», а не «сделай побыстрее»;
2. **названо, что именно гнать параллельно** — поимённый набор проходов
`specs` и `code` параллельно») или стадия целиком («стадию 1 параллельно»).
Просьба без набора — **не основание**: гоним последовательно и одной строкой
говорим, что набор не был назван. Это не придирка к формулировке. Параллелить
можно ровно то, что не мешает друг другу, а знание об этом лежит у того, кто
просит: он видит, занята ли машина, и ждёт ли он от прогона замеров. Домысливать
набор за него — значит принять решение, которое он оставил себе.
Почему умолчание именно такое:
- **Замеры.** Проходы `adversary` и `ops` доказывают находки числами: время
удержания блокировки против её таймаута, пик кучи против размера тела, темп
роста файлов журнала, длительность транзакции. Два меряющих прохода на одной
машине соревнуются за диск, CPU и за саму СУБД и выдают числа, которые не
воспроизведутся. Это не гипотеза: правило выведено из находок, целиком
державшихся на таких замерах, — у каждого проекта они свои и лежат в разделе
`## Прецеденты` его брифа. Число, снятое под конкурентную нагрузку от соседнего
прохода, — это находка с испорченным оракулом, а её опровержение стоит дороже
всего выигрыша от параллельности.
- **Машина одна.** Рядом идёт задача, поднят сервис, гоняется гейт или дорогая
проверка проекта.
- **Ранний выход** возможен только при последовательном прогоне (см. ниже).
- **Разбор самого конвейера.** Когда выясняется, почему проход чего-то не нашёл,
порядок и изоляция важнее скорости.
Если параллельный режим всё же включён, в границы покрытия идёт строка: какие
проходы шли разом и что замеры, снятые в этом прогоне, как оракул слабее.
**Чего режим не меняет — и это не подлежит обсуждению.** Проход **не видит**
находок других проходов ни в каком режиме. «Последовательно» значит «по
очереди», а не «следующий читает предыдущего». Вся ценность конвейера держится
на декорреляции: под всеми ролями одна модель с одними априорными, и стоит
показать ей чужой вывод — она согласится. Согласие нескольких проходов и так не
повышает `confidence` (см. «Честный предел»); согласие **наведённое** ещё и
маскируется под независимое подтверждение. Единственный, кто видит всё, — триаж,
и это его работа.
**Ранний выход** (последовательный режим делает его возможным — это его побочная
выгода, а не повод его выбирать). Допустимо остановить прогон, не докатив
остаток, ровно в одном случае: находка требует **переделки формы** изменения, и
остальные проходы будут смотреть на код, которого через час не станет. Тогда:
- прогон останавливается, находка чинится, конвейер запускается **заново с
нулевой стадии** — а не «доезжает» остатком по старому коду;
- незапущенные проходы идут в границы покрытия строкой «не запускался: прогон
остановлен на <проход> из-за <находка>», поимённо;
- триаж запускается только на полном прогоне. Отчёт триажа по половине проходов
выглядит полным, потому что агрегирует всё, что ему подали, — это тот же
молчащий пропуск, что и в разделе «Профили».
Ранний выход по находке, которая чинится в пределах существующей формы
(`Действие: инлайн`), **не делается**: дешевле дособрать все находки и починить
пачкой, чем гонять конвейер дважды.
Режим объявляется в отчёте наравне с профилем, и если он **параллельный**с
причиной и составом. Последовательный объявляется одним словом.
## Стадия 0 — Gate (обязательна во всех профилях)
Агент `review-gate`. Запускает команду гейта из раздела `## Гейт` брифа и
интерпретирует вывод.
**Пока гейт красный — опиниативные проходы не запускаются.** Оркестратор чинит и
перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки
(гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не
блокирует.
Гейт возвращает не только «зелено/красно», но и находки класса **отсутствующая
верификация**: изменённые строки без покрытия, конкурентность без теста с
параллельным доступом, флаки-тест (не ниже `major`), недоступный инструмент.
Шаги выбираются по изменённым файлам: правка документации не гоняет тесты,
линтеры и детектор гонок. Пропуск при этом не молчит — он виден в сводке с
причиной и уезжает в границы покрытия, как и любой другой `SKIP`.
Шаги, которые красят гейт безусловно, перечислены в брифе с причиной. Проходу
запрещено списывать такой отказ в мелочь.
## Стадия 1 — Conformance (обязательна во всех профилях)
Два applicative-прохода: оба применяют **записанный** критерий, оба дешёвые.
Замеров они не делают и потому безобиднее прочих, если параллельный режим
попросят с их именами; сами по себе идут по очереди, как и все.
- `review-specs` — критерий взят из **дельта-спек предлагаемого изменения**, а не
из proposal, сообщения коммита или описания задачи. Сверка двунаправленная;
направление `code → spec` важнее.
- `review-code` — критерий взят из конвенций проекта: файла или каталога файлов,
путь — раздел `## Карта` брифа. Берётся только та их часть, которая **не
выражается правилом**:
механизируемое уже проверила стадия 0. Что именно механизировано, тот же раздел
брифа перечисляет — повторять это проходом вредно.
Recall обоих равен длине их источника — это и есть предел applicative-проходов,
ради которого существует стадия 2.
## Стадия 2 — Adversarial и operational (`standard`, `deep`)
Два прохода:
- `review-adversary` — находка есть **построенный путь**, а не свойство;
- `review-ops` — постмортем от симптома у владельца сервиса к строке кода.
**Эту пару параллелить не стоит даже по просьбе — переспроси.** Оба доказывают
находки замером, и оба меряют одно и то же железо. Запущенные разом, они портят
числа друг другу, а испорченный оракул хуже отсутствующего: находка выглядит
доказанной. Если их всё же назвали в параллельном наборе — выполняй, но скажи в
границах покрытия, что числа этого прогона сняты под соседней нагрузкой.
**Эта стадия зарабатывает больше всех остальных вместе, и потому стоит в
`standard`, а не только в `deep`.** Измерено на пяти задачах подряд: враждебный
проход дал пять из семи выживших находок дозапуска (включая обе верхние);
эксплуатационный — единственный, кто нашёл, что откат бинаря поверх новой схемы
стартует молча. Оба несут внешний оракул по построению: один обязан путь
**прогнать**, второй смотрит ось времени и эксплуатации, которую не смотрит
никто другой.
Материал обоим даёт бриф: `## Модель угроз` — враждебному, `## Прод и поток`
эксплуатационному. Без этих разделов стадия вырождается в общие места.
## Стадия 3 — Independent reimplementation (`deep`, по триггеру)
- `review-reimpl` — пишет свою реализацию, не открывая существующую, затем
диффит по решениям. **Запускается по триггеру, а не всегда:** изменение вводит
новое правило идентичности, слияния или разбора (проектная формулировка
триггера — в разделе `## Триггеры` брифа). Это самый дорогой проход конвейера
(его счёт определяется объёмом вывода — он пишет реализацию целиком), а вне
этого триггера независимый взгляд в значительной мере уже дал профиль `design`:
код писался под его находки. Триггер выбран по факту: единственный раз, когда
триаж назвал отсутствие `reimpl` дырой покрытия, — это была задача с новым
правилом слияния сущностей.
## Стадия 4 — Global (`deep`, `design`)
Агент `review-architecture`. Получает **вход шире диффа**: дерево пакетов с
назначением, граф внутренних зависимостей, инвентарь существующих концепций.
Команду, которая это готовит, даёт раздел `## Команды` брифа; нет команды —
проход собирает карту сам и говорит об этом в границах покрытия.
Главный вопрос — концептуальная целостность и **второй способ** делать то, что
уже делается. Он же и оправдывает проход: на задаче про пересборку архитектурный
проход нашёл, что новый код был **вторым проигрывателем журнала** со своим
порядком. Второй обязательный вопрос — **что опытный человек отсюда удалил бы**:
слой с единственной реализацией, интерфейс ради мока, незапрошенная
конфигурируемость, подстраховка поверх подстраховки. Потолок — 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–14 свойств уже лежат приёмочными
критериями в `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/`). Он единственное, по чему потом видно, что
было найдено и что из этого не заведено: нулевой урожай при непустом отчёте
виден сразу.
## Честный предел
Модель воспроизводит медиану публичного кода, смещённую к популярному и
туториальному: отсюда тяга к интерфейсам ради интерфейсов, лишним мокам и
конфигурируемости, которую никто не просил. **«Идиоматично» и «распространено» —
разные вещи**; проходы обязаны различать их и опираться на поимённое положение
гайда, а не на ощущение частотности.
Согласие нескольких проходов — **не подтверждение**: это один источник,
высказавшийся несколько раз. Совпадение повышает приоритет, но не `confidence`.
Что недоступно **этому** проекту принципиально — перечисляет раздел
`## Недоступно проверке` брифа, и он целиком уезжает в границы покрытия.
Независимо от проекта недоступно:
- поведение внешних систем в их будущих версиях;
- реальный профиль нагрузки и то, что на самом деле лежит в данных;
- завязка внешних потребителей на текущую форму ответа;
- суждение «этой функциональности не должно существовать».
Отдельно и честно: **поимённая сверка с положениями стайлгайдов языка не
задаётся ни одним проходом.** Проход про идиоматичность упразднён, его способные
части переселены (эксперимент против поведения библиотеки и драйвера — в `ops`,
вопрос 8; «не изобретаем ли то, что уже есть в библиотеке» — в `architecture`,
вопрос 1), но различение «идиоматично против распространено» теперь не спрашивает
никто. Класс обратимый — портит форму кода, не данные, — и его надо признавать в
границах покрытия, а не считать проверенным.
Это и есть причина, по которой конвейер готовит ревью, а не заменяет его.
## Ссылки
- Skill `av-dev-pipeline:project-brief` — заведение и обновление брифа.
- [references/project-brief.md](references/project-brief.md) — контракт брифа проекта.
- [references/brief-template.md](references/brief-template.md) — шаблон брифа.
- [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) — журнал проскочивших дефектов.