Files
houseplan-card/docs/reviews/CODE-REVIEW-472-r2.md
T
2026-09-06 12:37:04 +00:00

151 lines
13 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.
# 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
```