docs: review document for #623

Issue: #623
User-Visible: no
This commit is contained in:
claude[bot]
2026-09-24 07:56:01 +00:00
parent baf283c50f
commit 79ee16546d
2 changed files with 181 additions and 1 deletions
+179
View File
@@ -0,0 +1,179 @@
# CODE-REVIEW-623-r1
Issue: #623 · этап code · заход r1 · блокирующих циклов израсходовано 0 из 4
Материал: `baf283c50f3edc305811e2074690830ef5da9d73` (tree `4e7c88e0c6b4ed59524a50676867e52bd1f3dbef` — совпадает с `HEAD^{tree}`, проверено `git cat-file -t`)
Трек: инфраструктурный, класс B (файлов класса A нет — подтверждено `git diff --stat`)
Validate на этом SHA: success (ссылка в задании ревью) — дешёвые гейты (`typecheck`, `npm test`, `npm run build` со сверкой бандла) приняты по этому прогону, не перегонялись.
## Скоуп
Из аудита 22.09 (#623): шесть workflow, исполняемых GitHub из ветки по
умолчанию (`main`) по событиям `issues`/`schedule`/`workflow_run`
(`process.yml`, `process-resume.yml`, `process-reconcile.yml`,
`mutation-gate.yml`, `nightly.yml`, `process-metrics.yml`), правились раньше
двумя ручными коммитами (26 mirror + 13 merge-back за месяц), preflight сверял
только 3 из 6. Автор выбрал вариант 2 из issue: тело каждого файла переносится
в `_<имя>.yml` (`on: workflow_call`), исходный файл остаётся тонким —
триггеры, `run-name`, потолок прав, `concurrency` — и вызывает тело по ссылке
`@dev` с `secrets: inherit`. Работа обслуживает инфраструктуру самого
конвейера ревью (docs/SCOPE.md эту область не описывает — она вне продукта,
что и делает задачу класса B без ограничения по Core user jobs).
AC из issue:
- AC1 — правка конвейера один коммит в одну ветку; preflight покрывает все
исполняемые из `main` файлы либо не нужен.
- AC2 — релизный путь без шага «merge main → dev».
- AC3 — документация (`PROCESS.md` §10, `AGENTS.md`) обновлена.
## Как проверялось
| Гейт | Команда | Результат | Кто прогнал |
|---|---|---|---|
| typecheck/test/build/бандл | — | success (Validate на `baf283c5`) | принято по ссылке, не перегонялось |
| YAML-валидность всех workflow | `python3 yaml.safe_load` по всем 18 `.github/workflows/*.yml` | 0 ошибок | ревьюер |
| Целевые/переименованные тесты | `node --test test/default-branch-workflows.test.mjs test/action-pins.test.mjs test/mutation-gate.test.mjs test/nightly-workflow.test.mjs test/process-metrics.test.mjs test/process-resume.test.mjs test/rebase-generated.test.mjs test/review-doc-guard.test.mjs test/review-result-gate.test.mjs test/reviews-index.test.mjs test/validate-workflow.test.mjs test/process-digests.test.mjs` | 257/257 pass | ревьюер |
| `entry-cost` (AGENTS.md правился) | `node --test test/entry-cost.test.mjs` | 3/3 pass | ревьюер |
| Мутационные якоря применимы | `node scripts/mutation-gate.mjs --check` | все ok | ревьюер |
| Пины сторонних Actions | `node scripts/action-pins.mjs` | «все сторонние Actions закреплены полным SHA» | ревьюер |
| **6 новых мутантов задачи** | `node scripts/mutation-gate.mjs --id=<name>` по каждому из `own-reusable-accepts-any-ref`, `workflow-sync-forgets-a-thin-caller`, `thin-caller-runs-the-main-body`, `thin-caller-widens-permissions`, `thin-caller-drops-secrets`, `mutation-body-keys-marker-on-caller-sha` | **6/6 «поймано 1 из 1»** | ревьюер, независимо от заявления автора |
| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | — | «Исполняемого frontend-диффа нет… Browser-smoke этим диффом не выбираются» | ревьюер |
| Факт GitHub Actions: существование `job.workflow_sha` | `curl` официальной страницы `docs.github.com/.../contexts` | подтверждено дословно: *«job.workflow_sha — The commit SHA of the workflow file that defines the current job»*, с примером именно для reusable workflow | ревьюер (не полагался на слова автора) |
Чтения (не исполнением, а построчным разбором): `.github/workflows/process.yml`,
`_process.yml` (1648 строк, целиком через diff со старым телом — совпадает
дословно за вычетом замены `github.workflow_sha`→`job.workflow_sha` в трёх
местах и снятия дублирующихся шапки/concurrency), `_mutation-gate.yml`,
`mutation-gate.yml`, `nightly.yml`, `process-metrics.yml`,
`process-reconcile.yml`, `process-resume.yml`, `validate.yml` (диф
`workflow_sync`), `scripts/action-pins.mjs`, `scripts/mutation-registry.mjs`,
`PROCESS.md` §10.4, `AGENTS.md`, `docs/process/REVIEWER.md`.
## AC → доказательство
| AC | Чем доказан | Чем краснеет | Проверка ревьюера |
|---|---|---|---|
| AC1: правка конвейера — один коммит в одну ветку | тела перенесены в `_*.yml` дословно, вызываются `@dev`; коммит `baf283c5` — единственный, только в `issue/623-...`→`dev` | мутант `thin-caller-runs-the-main-body` (подмена на локальную `./` ссылку) | **прогнан лично, поймано 1/1** |
| AC1: preflight покрывает все исполняемые из `main` файлы | `validate.yml` сверяет 6 файлов вместо 3; `test/default-branch-workflows.test.mjs` строит список файлов ПО ТРИГГЕРАМ (`DEFAULT_BRANCH_EVENTS`), а не по памяти, и сверяет его со списком preflight | мутант `workflow-sync-forgets-a-thin-caller` | **прогнан лично, поймано 1/1** |
| AC1: права не шире прежних, секреты доходят | тест «не расширяет права тела и не сужает их» строит объединение прав всех job тела и требует точного совпадения с потолком вызывающей job (в обе стороны) | мутанты `thin-caller-widens-permissions`, `thin-caller-drops-secrets` | **прогнаны лично, поймано 2/2** |
| AC1: `@dev` не открывает дверь перемещаемым ref | узкое регулярное исключение в `action-pins.mjs` (только `Matysh/houseplan-card`, только `_*.yml`, только `@dev`, только с комментарием), 2 новых теста в `action-pins.test.mjs` перебирают 7 «соседних» спецификаций (другой ref/репозиторий/путь/форк) и требуют находку по каждой | мутант `own-reusable-accepts-any-ref` (регэксп расширен на любой ref) | **прогнан лично, поймано 1/1** |
| AC1: маркер ночного reuse зависит от версии тела, а не от caller-файла в `main` | замена `github.workflow_sha`→`job.workflow_sha` в трёх местах `_mutation-gate.yml`; тест проверяет отсутствие `github.workflow_sha` в теле | мутант `mutation-body-keys-marker-on-caller-sha` | **прогнан лично, поймано 1/1**; семантика `job.workflow_sha` дополнительно подтверждена официальной документацией GitHub (см. таблицу гейтов) — риск «поля не существует» снят |
| AC2: релизный путь без «merge main → dev» | структурно достигнуто (правка конвейера больше не требует второго коммита в `main`); `AGENTS.md`: «no mirror into main, no merge-back before promotion» | не автоматизировано — зависит от разового ручного зеркалирования 6 файлов в `main` сразу после слияния (раздел «Осталось» в хендоффе) | проверено чтением, не исполнением — см. «Находки» ниже |
| AC3: документация | `PROCESS.md` §10.4 (новый абзац «Workflow из ветки по умолчанию»), `AGENTS.md` (раздел «Workflows run from the default branch are thin callers»), `docs/process/REVIEWER.md` (путь к `_process.yml`) | `test/process-digests.test.mjs`, `test/entry-cost.test.mjs` | **прогнаны лично, зелёные** |
## Находки
Нет High. Нет Medium ни в скоупе, ни вне его.
**Low (снята с записью, без правки).** AC2 создаёт гарантированное, но
одноразовое окно: сразу после слияния этой задачи в `dev` copy `main` останется
старой (полные тела), и preflight `workflow_sync` в `validate.yml` — жёсткий
гейт (`exit $fail`, не informational) — станет красным для **любого**
последующего прогона Validate в репозитории (не только для #623), пока кто-то
не отправит зеркальный коммит шести тонких файлов в `main` вручную. Автор
предвидел это и явно расписал в разделе «Осталось после слияния»
(«зеркалить сразу»). Не завожу как Medium, потому что: (1) автоматизировать
шаг нельзя без нарушения существующего правила AGENTS.md «pushing main…
requires the owner's explicit command» — то есть устранение этого окна вне
досягаемости этой задачи; (2) сам паттерн «красный preflight между двумя
ручными коммитами» — это статус-кво, которое и породило issue #623, а не
новый риск; (3) минимизация (сверка выросла с 3 до 6 файлов и стала полной)
и без того выполняет AC1. Рекомендация — не техническая правка, а
организационная: перед постановкой `S8-merged` на #623 владельцу стоит быть
готовым сразу же запушить шесть тонких файлов в `main`, как и написано в
хендоффе.
## Что проверено и корректно
- Материал ревью соответствует SHA задания (`git cat-file -t` дерева).
- Диф ограничен классом B (`test/**`, `scripts/**`, `.github/workflows/**`,
`PROCESS.md`, `AGENTS.md`, `docs/process/REVIEWER.md`) — файлов класса A нет.
- Единственный коммит несёт оба обязательных трейлера: `Issue: #623`,
`User-Visible: no` (верно — конвейер не продукт, changelog не требуется).
- Права: все job тела `_process.yml` (`guard`, `prepare`, `model_review`,
`integrate`) декларируют ровно `contents: read, issues: write` — union
совпадает с потолком вызывающей `dev`-job; ни одна job не требует
`contents: write` на уровне GITHUB_TOKEN (пуш идёт PAT-токеном
`HP_PROCESS_TOKEN` в явном виде, в обход разрешений раннера) — потолок
тонкого файла корректен без излишка.
- `if:`-фильтр вызывающей job у `process.yml` и `process-resume.yml` дословно
повторяет страж тела (`guard`/`resume`) — посторонние события остаются
`skipped`, поведение до задачи не изменилось.
- `performance.yml` обоснованно исключён из сверки: по расписанию судит
`main` собственным (несинхронизируемым) телом — тест это утверждает и
проверяет отсутствие `ref: dev` в файле.
- `test/default-branch-workflows.test.mjs` — новый файл, 228 строк, находит
«файлы из main» **по факту их триггеров** (множество событий
`DEFAULT_BRANCH_EVENTS`), а не по жёстко вписанному списку, и держит
проводку по восьми независимым инвариантам (тонкость, `@dev`, `secrets:
inherit`, права, входы, `if`, отсутствие `github.workflow_sha` в теле,
отсутствие сиротских тел/чужих вызовов) — все прогнаны лично.
- 6 новых мутантов задачи вручную прогнаны по одному (`--id=`), а не приняты
по заявлению автора: все 6 «поймано 1 из 1».
- Факт про `job.workflow_sha` (ключевая техническая посылка задачи —
«в вызываемом workflow `github.workflow_sha` принадлежит вызывающему
файлу из `main`, а не телу») подтверждён официальной документацией GitHub
дословно, а не принят на слово.
- Документация (`PROCESS.md` §10.4, `AGENTS.md`, `docs/process/REVIEWER.md`)
отражает новую схему; `process-digests`/`entry-cost` тесты, держащие
консистентность документов, зелёные.
- 18 workflow-файлов репозитория парсятся `yaml.safe_load` без ошибок.
## Чего не проверял
- **Живой прогон reusable-вызова на GitHub** — до слияния в `dev` файла
`_process.yml@dev` не существует, а тонкие файлы в `main` ещё старые;
живьём проверить нельзя было и мне. Компенсировано независимой сверкой
ключевой предпосылки (`job.workflow_sha`) с официальной документацией
GitHub и полным набором regex/структурных тестов с проверенными мутантами
— но фактическое поведение `github`-контекста (`event.issue`,
`run_id`, артефакты) внутри вызываемого job на реальном прогоне не
наблюдалось никем, включая автора.
- Полные `npm test`/`npx tsc --noEmit`/`npm run build` не перегонялись —
приняты по зелёному Validate на этом точном SHA (см. таблицу гейтов);
перепрогнан только точечный поднабор тестов, затронутый диффом.
- `golden:verify`, `pytest tests_backend`, инварианты модели, performance —
не запускались: диф не содержит `src/**`, Python и геометрии, в AC они не
названы.
- Организационное исполнение «зеркалировать шесть файлов в `main` сразу
после слияния» — не код, не проверяется тестом; полагаюсь на
зафиксированную инструкцию в хендоффе автора и AGENTS.md.
## Материал раунда
```
tree 4e7c88e0c6b4ed59524a50676867e52bd1f3dbef
blob c74c18ec3e940f43808cd833c1941732a705b81c .github/workflows/process.yml
blob dbe7457a67f1468d7fc4de9e577ba03e5d246881 .github/workflows/_process.yml
blob e40bf53fbbdd0714818c0e310332a6ba50e53e46 .github/workflows/mutation-gate.yml
blob 64189d81874a2929876b511fa83526593faea9e8 .github/workflows/_mutation-gate.yml
blob 3a0122c2c8b88127e02b43da7790997a88d42ae8 .github/workflows/validate.yml
blob 42357ba64c4852e622264c45c7be30b9d8ba2c18 scripts/action-pins.mjs
blob 743b5651621ca1818bb0c77792e6f6867e4210b6 test/default-branch-workflows.test.mjs
blob 92ee3d12e4fe024aaf35521e3d8e694a562d4858 scripts/mutation-registry.mjs
blob 06d4bfde32eebfc0bff7c6e555f44cf910ceed4a PROCESS.md
blob 2bad910126b75d6dc9ca0efdadf8b550fee7421f AGENTS.md
sha: baf283c50f3edc305811e2074690830ef5da9d73
```
## Вердикт
Зелёный. AC1–AC3 доказаны — большей частью автотестами, ключевые защитные
свойства перепроверены мутациями лично, а не приняты на слово; единственная
техническая посылка, не поддающаяся автотесту (семантика
`job.workflow_sha`), сверена с первоисточником. Единственное замечание —
организационное окно между слиянием и разовым зеркалированием в `main` —
снято с запиской, а не заведено как блокирующая находка.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/623-reusable-workflows`, коммит `baf283c50f3e` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `4e7c88e0c6b4ed59524a50676867e52bd1f3dbef`
```
git log --all --format='%H %T' | grep 4e7c88e0c6b4
```
- Тело issue: `f16c9db5059af784da7fda1de9ebb2c37379df683d14dbf04d2586683754e15a`
- Вердикт конвейера: `green` · High 0
+2 -1
View File
@@ -1,6 +1,6 @@
# Индекс ревью
Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1026, issue: 364. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`.
Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1027, issue: 365. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`.
| Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | Файлы |
|---|---|---|---|---:|---:|---|---|
@@ -35,6 +35,7 @@
| #624 | [SPEC-REVIEW-624-r1.md](SPEC-REVIEW-624-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | AC1-b: «чем краснеет» не соответствует заявленному свидетелю | `scripts/gate-small.mjs` `scripts/bundle-budget.mjs` `scripts/monolith-baseline.json` |
| #624 | [SPEC-REVIEW-624-r2.md](SPEC-REVIEW-624-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | — |
| #624 | [CODE-REVIEW-624-r1.md](CODE-REVIEW-624-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
| #623 | [CODE-REVIEW-623-r1.md](CODE-REVIEW-623-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
| #622 | [CODE-REVIEW-622-r1.md](CODE-REVIEW-622-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
| #621 | [CODE-REVIEW-621-r1.md](CODE-REVIEW-621-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
| #620 | [CODE-REVIEW-620-r1.md](CODE-REVIEW-620-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |