diff --git a/av-dev/agents/review-autotests.md b/av-dev/agents/review-autotests.md index 9921f35..ef4271b 100644 --- a/av-dev/agents/review-autotests.md +++ b/av-dev/agents/review-autotests.md @@ -1,6 +1,6 @@ --- name: review-autotests -description: "Тема `autotests` — проверено ли машиной и хватает ли проверок. Запускает команду гейта проекта (сборка/vet/линт/формат/тесты/флаки/гонки/покрытие изменённых строк/миграции/секреты/уязвимости) и интерпретирует вывод. Отличает новые отказы от унаследованных, находит отсутствующую верификацию (изменённые строки без покрытия, конкурентность без теста, флаки). Пока гейт красный, проходы с мнением не запускаются. Первый проход ревью кода и источник его графа, обязателен при любой метке." +description: "Тема `autotests` — проверено ли машиной и хватает ли проверок. Гонит команду гейта проекта (сборка/vet/линт/формат/тесты/флаки/гонки/покрытие изменённых строк/миграции/секреты/уязвимости) и интерпретирует вывод; прогон, сделанный до ревью, засчитывает по отпечатку рабочего дерева вместо повтора. Отличает новые отказы от унаследованных, находит отсутствующую верификацию (изменённые строки без покрытия, конкурентность без теста, флаки). Пока гейт красный, проходы с мнением не запускаются. Первый проход ревью кода и источник его графа, обязателен при любой метке." tools: Bash, Read, Grep, Glob model: sonnet color: green @@ -40,15 +40,42 @@ color: green `CLAUDE.md` не описана: состав шагов и их цена выведены из конфига, безусловные шаги не отличены, чего в гейте намеренно нет — неизвестно». +## Прогнан ли гейт уже + +**Задача приходит на ревью с зелёным гейтом:** сценарий, приведший её сюда, +довёл его до зелёного сам. Второй прогон на неизменившемся дереве вернёт тот же +вывод, а стоит он минут — правило и его причина в SKILL.md конвейера, ступень 1. + +Задание несёт сводку прошлого прогона, путь к логам шагов и **отпечаток дерева**, +снятый сразу после него. Сними отпечаток сам и сверь: + + + +```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 +``` + + + +**Совпал** — команду не запускай: читай готовую сводку и логи шагов, а тему +закрывай целиком, как обычно. **Разошёлся, отпечатка в задании нет, логи +недоступны** — гони гейт сам и ни у кого не спрашивай. + +Переиспользованный прогон объявляется строкой сводки и строкой границ покрытия: +чем гейт прогнан, когда и на каком отпечатке. + ## Что делаешь 1. Определи базу диффа: из задания, иначе `git merge-base HEAD <основная ветка>` (на основной ветке — `HEAD~1`). -2. Запусти команду гейта, передав ей базу. Она гонит все шаги до конца и печатает +2. Сверь отпечаток дерева — раздел «Прогнан ли гейт уже» выше. Совпал — + переходи к пункту 4 и работай по готовой сводке и логам. +3. Запусти команду гейта, передав ей базу. Она гонит все шаги до конца и печатает сводку; подробности — в логах шагов. -3. По каждому отказу открой лог и прочитай **реальную** причину. Не пересказывай +4. По каждому отказу открой лог и прочитай **реальную** причину. Не пересказывай строку «FAIL» — назови упавший тест, файл и утверждение. -4. **Отдели новое от унаследованного.** Если отказ выглядит не связанным с +5. **Отдели новое от унаследованного.** Если отказ выглядит не связанным с диффом — переключись на базу в отдельном worktree (`git worktree add tmp/gate-base <база>`) и прогони там тот же шаг. Отказ, воспроизводящийся на базе, — не блокер этого change: выводи его `minor` с @@ -117,10 +144,13 @@ color: green ## Формат вывода Сперва одной строкой: `ГЕЙТ: зелёный | красный` и таблица-сводка команды как -есть. Затем находки по контракту. В конце — обязательный блок: +есть. **Прогон переиспользован — скажи это той же строкой:** чем гейт прогнан, +когда и на каком отпечатке. Затем находки по контракту. В конце — обязательный +блок: ``` ## Coverage of this pass +- гейт: <прогнан здесь | переиспользован: чем, когда, отпечаток> - проверено: <перечисли выполненные команды> - не проверялось и почему: <шаги SKIP с причинами; проверки вне гейта> - принципиально недоступно этому проходу: замысел, форма решения, архитектура diff --git a/av-dev/skills/code-resolve/SKILL.md b/av-dev/skills/code-resolve/SKILL.md index c278fb8..ab59683 100644 --- a/av-dev/skills/code-resolve/SKILL.md +++ b/av-dev/skills/code-resolve/SKILL.md @@ -348,8 +348,12 @@ flowchart TD ### Возврат — не длиннее экрана -Агент возвращает: что сделано, **адресами** тронутого; исход гейта и чем он -прогнан; что не удалось и почему; вопросы, если по заданию их не разрешить. +Агент возвращает: что сделано, **адресами** тронутого; исход гейта, чем он +прогнан, где логи шагов и **отпечаток дерева сразу после прогона**; что не +удалось и почему; вопросы, если по заданию их не разрешить. Отпечаток нужен +ревью: по нему ступень автотестов засчитывает этот прогон вместо своего +(`av-dev:code-review`, ступень 1) — без него гейт гоняется дважды на том же +дереве. Диффа, пересказа кода и логов в возврате нет — иначе экономия, ради которой шаг и вынесен, отменяется в момент возврата. diff --git a/av-dev/skills/code-resolve/references/maintain.md b/av-dev/skills/code-resolve/references/maintain.md index 4c353cd..260b8cf 100644 --- a/av-dev/skills/code-resolve/references/maintain.md +++ b/av-dev/skills/code-resolve/references/maintain.md @@ -295,6 +295,10 @@ ADR: список источников канон закрыл двумя — а Прогони гейт и добейся зелёного — он же условие следующего шага: пока гейт красный, проходы с мнением не запускаются. +**Сразу после зелёного сними отпечаток дерева** (`av-dev:code-review`, ступень 1) +и сохрани его вместе со сводкой и путём к логам шагов. Шаг 4 передаёт их ревью, и +тогда ступень автотестов не гоняет тот же гейт второй раз. + **Поведенческая верификация здесь другая, чем в решении.** Проверяется не новое поведение, а то, что прежнее не поехало: команда из `CLAUDE.md` поднимается, шаг сборки отрабатывает, хук ставится на чистом клоне. **У оснастки, заводимой @@ -304,8 +308,9 @@ ADR: список источников канон закрыл двумя — а ### 4. Ревью — план фиксирован сценарием -Вызови Skill **`av-dev:code-review`**, дав базу диффа, режим и **план сценария**. -Change ты не передаёшь — его нет. +Вызови Skill **`av-dev:code-review`**, дав базу диффа, режим, **план сценария** и +**исход гейта с шага 3** — сводку, путь к логам шагов и отпечаток дерева. Change +ты не передаёшь — его нет. **Разметчик здесь не зовётся, и это правило, а не пропуск.** Обе оси, по которым он судит, у обслуживания не определены: размер он меряет по `proposal.md`, diff --git a/av-dev/skills/code-resolve/references/solve.md b/av-dev/skills/code-resolve/references/solve.md index f27c81b..769066f 100644 --- a/av-dev/skills/code-resolve/references/solve.md +++ b/av-dev/skills/code-resolve/references/solve.md @@ -254,7 +254,9 @@ flowchart TD **Код пишет агент, и в его же задании лежит весь этот раздел** (SKILL.md, «Кто пишет»): вызов `opsx:apply` для реализации `tasks.md`, гейт до зелёного, поведенческая верификация. Возврат — адреса тронутого, исход гейта и строка -верификации; диффа в нём нет. +верификации; диффа в нём нет. **Исход гейта возвращается сводкой, путём к логам +шагов и отпечатком дерева** (SKILL.md, «Возврат — не длиннее экрана»): его +передача на шаг 7 избавляет ревью от второго прогона того же гейта. Код — по конвенциям проекта (каталог `docs/conventions/`). Меняешь схему — обнови её описание в документации @@ -273,7 +275,8 @@ flowchart TD ### 7. Ревью кода — та же метка Вызови Skill **`av-dev:code-review`**, дав ссылку на change ``, -базу диффа, **план разметки с шага 3** и режим запуска. +базу диффа, **план разметки с шага 3**, режим запуска и **исход гейта с шага 6** — +сводку, путь к логам шагов и отпечаток дерева. **Метку ты не выбираешь, и это правило, а не упрощение.** Её назвал `review-scope` ещё на шаге 3 — по размеру и сложности, с обоснованием по каждой diff --git a/av-dev/skills/code-review/SKILL.md b/av-dev/skills/code-review/SKILL.md index 2045f99..1613758 100644 --- a/av-dev/skills/code-review/SKILL.md +++ b/av-dev/skills/code-review/SKILL.md @@ -221,6 +221,10 @@ description: "Конвейер ревью изменения, устроенны - **метка, его глубина и режим** прогона — чтобы проход знал, что писать в границы покрытия. +**Ступень 1 получает сверх этого исход гейта, прогнанного до ревью** — сводку, +путь к логам шагов и отпечаток дерева, — если вызывающий скилл его дал. Зачем и +что происходит при расхождении — «Ступень 1 — Автотесты». + Чего проход **не** получает ни в каком режиме — выводов других проходов. См. «Порядок прогона». @@ -734,8 +738,9 @@ change**: у работы, не меняющей поведения, дельт ## Ступень 1 — Автотесты (обязательна при любой метке) -Агент `review-autotests`, тема `autotests`. Запускает команду гейта из семантики -гейта в `CLAUDE.md` и интерпретирует вывод. +Агент `review-autotests`, тема `autotests`. Гонит команду гейта из семантики +гейта в `CLAUDE.md` — либо засчитывает прогон, сделанный до ревью, — и +интерпретирует вывод. **Тема и проход названы одинаково намеренно, а «гейт» осталось именем команды.** Раньше тема звалась `autotests`, а проход — `gate`: одна сущность под двумя @@ -743,6 +748,42 @@ change**: у работы, не меняющей поведения, дельт Слово «гейт» теперь значит ровно одно — барьер, который проект запускает; тема шире него ровно на «чего в гейте намеренно нет». +**Гейт, прогнанный до ревью, второй раз не гоняется.** Задача приходит на ревью +с зелёным гейтом: сценарий решения доводит его до зелёного шагом `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` закрывается целиком: +логи проход читает сам, находки об отсутствующей верификации выдаёт как обычно. +Переиспользование он объявляет строкой сводки и строкой границ покрытия — чем +гейт прогнан, когда и на каком отпечатке. Молчащее переиспользование неотличимо +от собственного прогона, а разница между ними в том, кто видел вывод своими +глазами. + **Пока гейт красный — проходы с мнением не запускаются.** Оркестратор чинит и перезапускает гейт. Исключение одно: отказ, унаследованный от базовой ветки (гейт проверяет это прогоном на базе) — тогда он фиксируется находкой и не diff --git a/decisions/73-gate-run-reused-by-fingerprint.md b/decisions/73-gate-run-reused-by-fingerprint.md new file mode 100644 index 0000000..f9c85fd --- /dev/null +++ b/decisions/73-gate-run-reused-by-fingerprint.md @@ -0,0 +1,68 @@ +# 73. Гейт, прогнанный до ревью, не гоняется второй раз (2026-08-23) + +## Что было + +Гейт проекта прогонялся дважды на одном и том же дереве. Сценарий решения +доводит его до зелёного шагом `opsx:apply`, сценарий обслуживания — своим шагом +гейта; следом ступень автотестов конвейера запускала ту же команду заново. + +Замер по прогонам в проекте transcriber 23 августа: ступень автотестов заняла 5 +минут 51 секунду при ревью кода в 62 минуты. Дерево между двумя прогонами не +менялось ни разу — код писал агент, возвращал зелёный гейт, и до ревью его никто +не трогал. + +Повтор держался не на доводе, а на умолчании: проход запускает команду, потому +что так написано в его уставе, и никто не спрашивал, откуда взялось дерево, на +котором он её запускает. + +## Решено + +**Р274. Гейт, прогнанный до ревью, второй раз не гоняется.** Переиспользуется +**команда, а не проход**: тема `autotests` закрывается целиком — логи шагов +проход читает сам, находки об отсутствующей верификации выдаёт как обычно. Дом +правила — ступень 1 конвейера, `av-dev:code-review`. + +**Р275. Признак — отпечаток рабочего дерева, и он проверяемый.** Отпечаток +снимают дважды: тот, кто прогнал гейт, — сразу после прогона, проход — перед +началом работы. Совпали — прогон засчитан, разошлись — проход гонит гейт сам. +Команда живёт домом в ступени 1 и помеченной копией в уставе `review-autotests`; +дословность держит `copies.py`, а не обещание. + +**Р276. Это не отмена правила «возврату на слово не верят» +([тема 72](72-writing-delegated-to-agents.md), Р270), и вот почему.** На слово +здесь не верят никому: гейт — вывод инструмента, а не проза агента, логи шагов +проход открывает сам, и отпечаток отвечает ровно на тот вопрос, который делает +чужой прогон доказательством, — **относится ли этот вывод к этому дереву**. +Правило Р270 запрещает верить пересказу; засчитывается же не пересказ, а +воспроизводимый артефакт с проверенной привязкой. + +**Р277. Отказ безопасен по построению.** Любое расхождение — отпечатки не +совпали, отпечатка в задании нет, логи недоступны — ведёт к полному прогону, и +проход не спрашивает разрешения. Лишний прогон стоит минут, засчитанный чужой — +красноты, которой никто не увидел; асимметрия цены и задаёт направление отказа. + +**Р278. Временный каталог из отпечатка выпадает сам.** `--exclude-standard` +отбрасывает игнорируемое, а логи шагов гейт пишет туда же. Проект, держащий +временный каталог под git, отпечатками не совпадёт никогда — и получит честный +прогон вместо тихого засчитывания. Настройки у этого нет намеренно: выключатель +здесь стал бы способом засчитать прогон, который засчитывать нельзя. + +**Р279. Переиспользование объявляется строкой** — в сводке прохода и в границах +покрытия: чем гейт прогнан, когда и на каком отпечатке. Молчащее +переиспользование неотличимо от собственного прогона, а разница между ними в том, +кто видел вывод своими глазами. + +## Следствия + +**С254. Возврат агента письма пополнился отпечатком.** К исходу гейта и тому, чем +он прогнан ([тема 72](72-writing-delegated-to-agents.md), Р269), добавились путь к +логам шагов и отпечаток дерева. Возврат от этого не удлинился на экран: обе вещи +— строки, а не содержимое. + +**С255. Оркестратор передаёт исход гейта в ревью.** Шаг 7 решения и шаг 4 +обслуживания несут его вместе с планом, базой диффа и режимом. Не передал — +конвейер прогонит гейт сам, и это стоит минут, а не корректности. + +**С256. Экономия названа замером, а не обещанием.** Снятый повтор — минуты одного +прогона из шестидесяти двух; остальное время ревью держат проход враждебных +постановок, цепочка за машину и триаж, и они разбираются отдельно. diff --git a/decisions/README.md b/decisions/README.md index 63cf15b..c093702 100644 --- a/decisions/README.md +++ b/decisions/README.md @@ -124,3 +124,4 @@ | 70 | [«Провенанс» снят из словаря: одного синонима мало для проверки](70-provenance-word-removed.md) | 2026-08-13 | | 71 | [Образец языка назван прямо; «интейк» снят вслед за «провенансом»](71-language-model-popular-science.md) | 2026-08-13 | | 72 | [Письмо уходит агентам: оркестратор ставит задание и читает возврат](72-writing-delegated-to-agents.md) | 2026-08-22 | +| 73 | [Гейт, прогнанный до ревью, не гоняется второй раз](73-gate-run-reused-by-fingerprint.md) | 2026-08-23 |