gate.sh и review-context.sh переписаны на python3 — в scripts/ уже жил diff-coverage.py, а разбор вывода git и сборка сводки на shell читались хуже, чем работали. Гейт больше не гоняет go-шаги впустую: build, vet, lint, gofmt, тесты, -race, покрытие и govulncheck запускаются, только если в диффе есть .go либо go.mod/go.sum; миграции — если тронуты миграции или код. Правка документации проходит гейт за секунды вместо минуты. Пропуск при этом не молчит: он в сводке с причиной и уезжает в границы покрытия, а charter гейта различает «код не трогали» (корректно) и «инструмента нет» (настоящая дыра). Изменённые файлы считаем как объединение диффа с базой, рабочего дерева и новых файлов: гейт гоняют и до коммита, и после, а лишний прогон шага дешевле пропущенного. Заодно govulncheck перестал рапортовать «уязвимостей: 0» когда он просто не смог отработать из-за несобирающегося кода — это SKIP, а не WARN. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
10 KiB
Отчёт о переработке конвейера ревью (2026-07-23)
Что было, что стало и на основании чего. Обоснование «почему» — ADR-2026-07-23-review-pipeline-generative.
Было
Отдельного скилла ревью не существовало. Ревью — это два сабагента
(jellybit-review-specs, jellybit-review-code), вызываемые из шагов 4 и 7
task-pipeline, плюс дубль в финальной сверке task-batch. Детерминированные
проверки жили отдельно и после опиниативных: lefthook срабатывал на
коммите, task test/task lint — внутри apply.
Диагностика показала: все проходы applicative, generative нет ни одного; ни один
проход не запускает инструменты (обоим это было прямо запрещено); сверка со
спекой односторонняя; архитектурный угол судит по диффу; триажа нет; границ
покрытия нет; измерения качества нет. Плюс два мелких долга: ссылка на
несуществующий скилл verify и дублирующие проходы в task-batch.
Стало
| Было | Стало | На основании чего |
|---|---|---|
task test/task lint внутри apply, lefthook на коммите |
Stage 0 jellybit-review-gate + task gate: build, vet, lint, gofmt, тесты, повтор на флаки, -race, покрытие изменённых строк, миграции, ER-схема, gitleaks, govulncheck. Блокирует опиниативные проходы |
детерминированный оракул надёжнее мнения; проверка, идущая после ревью, не защищает ревью |
jellybit-review-specs: spec → code |
Stage 1 он же + code → spec (тихие ветки, самодеятельные дефолты, проглоченные ошибки, незаказанные ретраи), границы спеки, право сказать «требование неверно» | системная болезнь агентского кода — тихо добавленное поведение; односторонняя сверка его не видит |
| — | Stage 2 jellybit-review-rubric, jellybit-review-reimpl, jellybit-review-idiom, jellybit-review-negative |
recall чек-листа равен его длине; неявный слой достаётся только порождением критерия |
архитектура как один из 9 буллетов review-code, вход = дифф |
Stage 3 jellybit-review-architecture, вход = task review:context (пакеты, граф зависимостей, инвентарь концепций) + дифф. Потолок 3 находки |
агент, видящий только дифф, не знает словаря проекта и потому не может судить о втором способе делать то же самое |
| — | Stage 4 jellybit-review-adversary (находка = построенный путь), jellybit-review-ops (условный постмортем) |
враждебная постановка находит то, чего не находит перечисление свойств |
| разгребал оркестратор вручную | Stage 5 jellybit-review-triage: дедуп по причине, оракул для critical/major, понижение неподтверждённого, отсев вкусовщины, потолок 7, разметка инлайн/развилка |
отчёт читает оркестратор и молча реализует прочитанное: без потолка узкое место переезжает в незаказанные правки кода |
review-code: 9 углов, включая механизируемое |
review-code сжат до конвенций, не выраженных правилом |
всё, что проверяет линтер, в промпте только отвлекает внимание |
ревью-проходы дублировались в task-batch |
в task-batch осталось только то, что появилось от слияния: рассинхроны на стыках + вопрос о втором способе |
те же проходы на тех же файлах дают те же находки и удорожают триаж |
ссылка на несуществующий скилл verify |
Skill run |
скилла verify нет ни в проекте, ни у пользователя — шаг молча не выполнялся |
Что слито и что удалено
- Слито: архитектурный угол и стиль/дублирование выведены из
jellybit-review-codeв отдельные проходы с разными классами дефектов; per-capability прогон вtask-batchсужен до стыков вместо повторного полного ревью. - Удалено: ни одного прохода. Существующий проход не удаляется без замера —
сначала калибровка (
calibration.md), потом решение.jellybit-review-codeостался стадией 1 рядом сjellybit-review-specs: оба applicative, критерий у обоих записан, только источники разные (дельта-спека и конвенции).
Правка по итогам самопроверки (2026-07-23)
Разбор собственной работы нашёл три избыточности; все три устранены:
jellybit-review-codeне запускался ни в одном профиле — charter обещал «проход профиляquick», аquickсостоял из стадий 0, 1, 5. Проход, который нельзя запустить, нельзя и откалибровать. Включён стадией 1.review-contextвыгружалgo doc -shortпо всему модулю — 264 строки из 458. Убрано: граф зависимостей, который иначе не восстановить, — это 21 строка, а публичную поверхность агент вытянетgo docсам по нужному месту.- Из
calibration.mdубрана секция «дополнительных метрик» (precision, корреляция, стоимость): показатели, которые никто не считает, изображают измеряемость вместо того, чтобы её давать.
Под подозрением остались jellybit-review-idiom (собственные правила загоняют
почти все его находки в minor) и половина вопросов jellybit-review-ops
(рост объёма в 50 раз для однопользовательского домашнего сервиса умозрителен).
Не тронуты намеренно: удалять проход по ощущению, а не по замеру — ровно то,
против чего написана процедура калибровки.
Конвенции → правила
Механизировано и вычеркнуто из прозы (docs/conventions/*) и из
openspec/config.yaml:
| Правило | Инструмент | Откуда убрано |
|---|---|---|
msg — константа, без интерполяции; стиль ключ-значение; ошибка полем |
sloglint |
logging.md |
slog вместо fmt.Print* |
forbidigo |
logging.md |
| конфиг не из env | forbidigo (os.Getenv) |
config.md |
время только через store.Now() |
forbidigo (time.Now) |
database.md |
err == ErrX, приведение типа ошибки |
errorlint |
errors.md |
| сторонние пакеты ошибок | depguard |
errors.md |
| матчинг ошибки по тексту сообщения | internal/archrules |
errors.md |
AUTOINCREMENT, DEFAULT (datetime('now')) в новых миграциях |
internal/archrules |
database.md |
| транспорты не знают друг о друге, ядро не знает о транспортах | internal/archrules |
CLAUDE.md (осталась одна строка принципа) |
| ER-схема обновлена вместе с миграцией | scripts/gate.py (по диффу) |
— |
Правки кода под новые правила: logging.StartCall как единая точка отсчёта
длительности внешних вызовов, store.Now вместо time.Now в httpapi и
часах воркера, slog.DiscardHandler в тестах.
Что осталось непокрытым намеренно
- Ревьювер наименований по словарю единого языка — глоссария нет, проверять не по чему. Заводится после задачи «Словарь единого языка».
- Дробление
review-codeна узкие оптики — отклонено: декорреляция внимания без декорреляции суждения почти не добавляет recall, но линейно удорожает триаж. - Профиль нагрузки, история инцидентов, завязка внешних потребителей — недоступны ни одному проходу и остаются человеку. Перечислены в разделе «Честный предел» скилла.
- Калибровка проходов не проведена: процедура заведена, первые прогоны — за пользователем (журнал проскочивших дефектов пока пуст).