docs: review document for #472

Issue: #472
User-Visible: no
This commit is contained in:
claude[bot]
2026-09-06 12:37:04 +00:00
parent 57a4a0ed69
commit 88e4cf50e3
+150
View File
@@ -0,0 +1,150 @@
# CODE-REVIEW-472-r2
Issue: #472 — «Полный мутационный прогон по расписанию падал дважды подряд без
реакции: у отказа нет адресата». Лёгкий трек (`small`). Код-ревью, заход r2,
блокирующих циклов израсходовано 1 из 2.
## Материал раунда
- SHA материала: `57a4a0ed69d01f0e1bf710f44f287cdc32c1e8fd` (`git rev-parse HEAD`)
- Предыдущий заход (r1): `docs/reviews/CODE-REVIEW-472-r1.md`, вердикт —
жёлтый, найдена M1 (SHA отчёта берётся из `github.sha`, а для `schedule`
это вершина `main`, не `dev`), проверено на `dec6c03e89af48f570bf60994b99aeacba0cb966`.
- **Этот SHA (`dec6c03e…`) сегодня недостижим ни из одной ветки/тега** —
проверено `git cat-file -t dec6c03e` (объект не найден) и `git log --all`.
Ветка была ещё раз приведена к `dev` конвейером между r1 и r2 (тот же
механизм, что уже осиротил `a8ea3b8a` между исходной реализацией и r1 —
«material-anchors», PROCESS.md §7.2). Восстановил доверие к преемственности
не по названию SHA, а по содержимому: коммит `e76f3909` («ci: отчёт об
отказе расписания мутантов…», текущий предок делты) даёт **побайтово тот
же `git diff --stat`**, что зафиксирован в «Скоуп диффа» CODE-REVIEW-472-r1.md
(105/28/177/34/99/49 строк по тем же шести файлам) — содержимое,
прорецензированное в r1, и содержимое под `e76f3909` — один и тот же текст,
просто с новым SHA после ребейза. Дельта раунда — ровно один коммит поверх
этого дерева:
- `57a4a0ed ci: SHA отчёта mutation-gate — от чекаута dev, не github.sha (#472 r1)`
— единственный коммит после `d77d1953` (публикация документа r1).
- Трейлеры дельты: `Issue: #472`, `User-Visible: no` — верно, диапазон CI-only,
changelog не тронут.
- Зелёного `Validate` на `57a4a0ed` нет (см. системное примечание к заходу) —
дешёвые гейты прогнаны мной, см. ниже.
## Дельта раунда
```
.github/workflows/mutation-gate.yml | 5 ++++-
test/mutation-gate.test.mjs | 8 ++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
```
```diff
- SHA: ${{ github.sha }}
run: |
+ # SHA — того дерева, которое чекаутили и гоняли (dev), а не
+ # github.sha: для расписания это вершина default-ветки main, и отчёт
+ # называл бы «dev @ <SHA main>» (ревью r1). Образец — process.yml.
+ SHA=$(git rev-parse HEAD)
mkdir -p artifacts
node scripts/mutation-gate-report.mjs \
--logs=artifacts/mutation-logs --shards=4 \
--run-url="$RUN_URL" --ref=dev --sha="$SHA" \
```
плюс новый контрактный тест `#472 r1: SHA отчёта — от чекаута dev, а не github.sha`.
Диапазон не тронул ничего за пределами `mutation-gate.yml` и одного тестового
файла: ни `src/**`, ни `custom_components/**/*.py`, ни другие workflow.
## Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| **M1** — SHA в отчёте брался из `github.sha` (вершина `main` для `schedule`), а не из чекаутнутого `dev` | Строка `env: SHA: ${{ github.sha }}` убрана из блока `env`; вместо неё в теле шага `SHA=$(git rev-parse HEAD)`, выполняется **после** `actions/checkout@v7` с `ref: dev` (проверено чтением: шаг checkout идёт раньше в том же job `report`, `mutation-gate.yml:124-153`) | `.github/workflows/mutation-gate.yml`, шаг «Собрать отчёт» job `report`; подтверждено исполнением — новый тест «17 pass» при фиксе, и я вручную откатил строку на `${{ github.sha }}` в рабочей копии: тест падает (17 pass / **1 fail**), значит утверждение теста небутафорское |
| **L1** — поиск открытого issue по заголовку (кириллица) не проверен вживую против реального GitHub API | Не код-находка, а решение: автор явно принял риск («Low — принято, понаблюдаем на первом реальном отказе») — сам r1-документ и автор согласились, что это не блокирует и альтернативы без сетевого доступа нет | Комментарий автора в issue от `12:17:55Z`: «L1 — принято, понаблюдаем на первом реальном отказе»; изменений кода не требовалось и не вносилось |
Оба закрытия проверены не по заявлению автора: M1 — исполнением теста в обе
стороны (падает без фикса, проходит с фиксом), L1 — это принятый риск с явной
записью, а не пропуск.
## Унаследовано из r1
Всё, чего не касается делта (весь диапазон, кроме `env: SHA` и одного нового
теста), наследуется из `docs/reviews/CODE-REVIEW-472-r1.md` (SHA материала
`dec6c03e`, содержимое эквивалентно текущему `e76f3909` — см. сверку diff-stat
выше) без повторной проверки:
- **AC1** (concurrency-группы `schedule`/`workflow_dispatch` разные) — контрактный тест + чтение.
- **AC2** (лог шарда — артефакт, `if: always()`, `PIPESTATUS`/`pipefail` сохраняет код выхода) — контрактный тест + чтение.
- **AC3** (сбор `escaped`/`redGuards`/`unparsed`, дедуп, статус `missing`) — 8 юнитов + 3 мутанта задачи, каждый прогнан индивидуально «поймано 1 из 1» на реальном исполнении (изолированный git-worktree).
- **AC4** (заголовок с маркером, тело — прогон/SHA/`--id=`) — юнит; замечание r1 (SHA неверный) устранено делтой этого раунда, сама структура тела не менялась.
- **AC4b** (отдельные разделы `redGuards`/`unparsed`, ни один не превращается в `--id=`) — юниты + мутант `mutation-report-red-guard-as-mutant`, прогнан индивидуально.
- **AC5** (job `report`: только `schedule` и не-успех, права `contents/actions/issues` полностью) — контрактный тест + чтение.
- **AC6** (повторный отказ — комментарий, не второе issue) — контрактный тест на порядок вызовов `gh` + `jq`-фильтр воспроизведён локально на фикстуре автором ревью r1.
- **AC7** (нет Telegram-секретов — не отказ) — контрактный тест + чтение условного шага.
- **AC8** (`mutation-gate.yml` под сверкой `main`/`dev` наравне с `process.yml`) — контрактный тест + чтение `validate.yml`.
- Плюмбинг артефактов (`upload-artifact` → `download-artifact` → путь, который читает `mutation-gate-report.mjs`) — прослежен логически в r1.
- Откат («Одним коммитом») — не менялся этой делтой.
Эти пункты дельта не задевает: делта — это одна переменная окружения и один
тест поверх неизменного контракта. Причин перепроверять AC1-AC3, AC5-AC8 нет —
код, который их доказывает, не тронут.
## Гейты этого раунда
| Гейт | Команда | Результат |
|---|---|---|
| typecheck | `npx tsc --noEmit` | зелёный, 0 ошибок |
| unit (полный набор) | `npm test` | 2107 тестов, pass 2106, fail 0, skipped 1 |
| unit (точечно, дельта) | `node --test test/mutation-gate.test.mjs` | 18/18 pass; при ручном откате `SHA=$(git rev-parse HEAD)` → `SHA=${{ github.sha }}` — 17 pass / **1 fail** (новый тест ловит регресс) |
| build + сверка 3 копий бандла | `npm run build` (tsc+rollup), затем `node scripts/bundle-sync.mjs` | build зелёный; `bundle-sync` не оставил diff в `custom_components/houseplan/frontend` и `demo/srv/assets` — три копии уже совпадали |
| provenance | `node scripts/validate-commit-provenance.mjs 57a4a0ed~1..57a4a0ed` | exit 0 |
| smoke-select | `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | «Исполняемого frontend-диффа нет… смоки этим диффом не выбираются», тронуто файлов: 7 — браузерные смоки не нужны |
Диапазон `origin/dev..HEAD` (не только делта раунда) не трогает `src/**` —
`check-docs`, `no-new-any`, golden, backend pytest, model-invariants,
perf-профили и полный (255) мутационный прогон неприменимы по тем же причинам,
что и в r1 (см. «Чего не проверял» там); делта этого раунда их тем более не
касается.
## Что проверено и корректно
- M1 закрыта корректно: `SHA=$(git rev-parse HEAD)` выполняется после
`checkout … ref: dev` в том же job — вычисленное значение соответствует
дереву, которое реально гоняли мутанты, а не вершине `main`.
- Новый тест умеет падать: проверено вручную откатом строки в рабочей копии
(17/18 → 1 fail), а не «Verified» без исполнения.
- Трейлеры дельты (`Issue: #472`, `User-Visible: no`) верны; changelog не
тронут — корректно для CI-only правки.
- Дешёвые гейты (typecheck/unit/build+3 копии бандла/provenance) зелёные на
`57a4a0ed`.
- Унаследованные AC1-AC8+AC4b не задеты делтой — перепроверка не требовалась.
## Чего не проверял
- Живое поведение `gh issue list --search` против реального API (L1) — не
проверялось ни в r1, ни сейчас: принятый риск, не в скоупе делты.
- Полный (255) мутационный прогон — предрелизный гейт, не гейт ревью.
- `check-docs`, golden, backend pytest, browser-smokes, perf-профили — diff не
трогает `src/**`/Python/визуал/perf-путь; `smoke-select` подтверждает
отсутствие исполняемого frontend-диффа.
- Зеркалирование `mutation-gate.yml` в `main` после мержа — ручной постмерж-шаг
вне этого коммита, как отмечено в r1.
## Вердикт
Зелёный. Единственная находка r1 (M1) закрыта по существу и подтверждена
исполнением (тест падает без фикса, проходит с фиксом); L1 — принятый автором
риск без кода. Дельта раунда минимальна и не затрагивает ничего, что требовало
бы переоткрытия AC1-AC8. Дешёвые гейты зелёные.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/472-mutation-gate-report`, коммит `57a4a0ed69d0` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `f6df55ea4b6305533915ed7682917d70322708e3`
```
git log --all --format='%H %T' | grep f6df55ea4b63
```