Files
houseplan-card/docs/reviews/CODE-REVIEW-555-r2.md
T
2026-09-13 13:01:27 +00:00

10 KiB
Raw Blame History

CODE-REVIEW #555 · r2

Материал: d0ecd5383155bfa39c84d66961f89c8f829db279 (рабочая копия — HEAD detached на этом SHA). Предыдущий раунд: r1, вердикт красный, документ на e3bf893e3d76b3276a548ee9182f073c901a12e9 (см. комментарий issue от 2026-09-13T12:43:17Z).

Скоуп раунда

r1 нашёл один High: в relabel() вторая («rollback») попытка --add-label не проверялась, а addComment() стоял в коде после relabel() — при отказе восстановления диагностика не публиковалась и терялась запись в summary.json, создавая риск бесконечного молчаливого цикла retry без единой эскалации. Остальная матрица decideReconciliation и race-guard «перечитать перед записью» были приняты r1 без замечаний.

Дельта r1→r2 (git diff e3bf893e..d0ecd538 --stat):

.github/workflows/process-reconcile.yml |   1 +
docs/reviews/CODE-REVIEW-555-r1.md      | 242 ++++++++++++++++++++++
scripts/mutation-gate.mjs               |  35 +++++
scripts/process-reconcile.mjs           |  76 +++++++---
test/process-reconcile.test.mjs         |  64 ++++++++-
test/review-doc-guard.test.mjs          |   2 +

Дельта локальна: правка одной функции записи (relabel), выделение write-path в applyReconciliationDecision, обработка ошибки в reconcileAll и один if: always() в workflow. Логика decideReconciliation (матрица фикстур) не тронута — полный разбор не требуется, разбираю дельту и то, до чего она дотягивается (сам write-path и его тесты).

Закрытие раунда r1

находка r1 чем закрыта где видно
Вторая попытка --add-label не проверялась, ошибка первой маскировала реальный результат relabel() теперь использует результат второй попытки как авторитетный: if (restore.status !== 0) throw ... scripts/process-reconcile.mjs:296-305 (было: безусловный throw с данными первой попытки)
addComment() шёл после relabel() → при отказе восстановления диагностика не публиковалась Write-path вынесен в applyReconciliationDecision: сначала ops.comment(...), затем (только для retry) ops.relabel(...) scripts/process-reconcile.mjs:314-320
Исключение из relabel/addComment не перехватывалось в reconcileAll, весь снимок тика терялся Вызов обёрнут в try/catch; ошибка кладётся в record.error, инкрементирует failures, цикл for продолжается scripts/process-reconcile.mjs:375-383
--output=summary.json не писался при исключении → upload-artifact без if: always() терял снимок CLI пишет summary.json до установки process.exitCode; exitCode=1 выставляется только после записи файла, если summary.failures scripts/process-reconcile.mjs:415-423
upload-artifact не имел if: always() Добавлен if: always() к шагу «Опубликовать компактный machine-readable итог» .github/workflows/process-reconcile.yml:52

Находка была в скоупе задачи и правится в этой же ветке — отдельный issue не заводился (соответствует правилу для High без выхода за скоуп).

Как проверялось

Прочитан полный diff git diff e3bf893e..d0ecd538 (все 6 файлов). Прогнано точечно (Validate на этом SHA уже green — см. ниже, полный набор не перегонял):

  • node --test test/process-reconcile.test.mjs — 11/11 green, включая 3 новых теста на write-path (порядок comment→relabel при отказе комментария, авторитетность второй попытки --add-label, сохранение ошибки в summary без обрыва снимка).
  • node --test --test-name-pattern="#555" test/review-doc-guard.test.mjs — 1/1 green, включая новую проверку if: always() в workflow-файле; эта проверка умеет падать — строки if: always() не было в файле на SHA r1 (видно из diff).
  • Три новых mutation witness прогнаны точечно через node scripts/mutation-gate.mjs --id=<id> для каждого из process-reconcile-relabels-before-durable-marker, process-reconcile-ignores-failed-label-restore, process-reconcile-write-error-aborts-summary — все 1/1 «поймано», подтверждает заявление автора «3/3».
  • Прочитан весь итоговый reconcileAll/relabel/applyReconciliationDecision построчно — подтверждено, что record.applied по умолчанию false, ошибка одной задачи не растёт в mutations, failures не сбрасывается между итерациями цикла, alreadyReported защищает от повторной публикации комментария на следующем тике (комментарий уже сохранён к моменту отказа relabel — проверено чтением, не исполнением, т.к. это требует реального GitHub API).
  • Проверено git diff --stat -- src/ — пусто, docs-гейт (check-docs.mjs) не требуется. Геометрия/layout/marker.space/толщины не задеты — инварианты модели не требуются. Diff не касается рендера — golden не требуется. custom_components/**/*.py не тронут — pytest не требуется. Смоки не применимы (не браузерный код).

Чего не проверял

  • Полный npm run typecheck / npm test / npm run build не перегонял — Validate на d0ecd538 подтверждён зелёным (https://github.com/Matysh/houseplan-card/actions/runs/34758274801), код с тех пор не менялся.
  • Полный ночной mutation-gate (все ~200+ мутантов) не гонял — вместо этого точечно проверил 3 новых мутанта, относящихся к делте; остальные не касаются изменённого кода.
  • Реальный прогон workflow process-reconcile.yml на живой очереди GitHub Actions (нет доступа/не требуется по PROCESS.md — ручное тестирование в цикле не предусмотрено).
  • Расхождение SHA в ссылках на Validate (автор в комментарии r2 привёл .../actions/runs/34758118947, а этому раунду дан .../actions/runs/34758274801) не расследовал отдельно: обе ссылки относятся к одному SHA d0ecd538, а прогон, названный ревьюеру как подтверждённый для этого материала, зелёный — этого достаточно для решения не перегонять дорогие гейты.

Унаследовано из r1

Без повторной проверки приняты (документ r1: комментарий issue от 2026-09-13T12:43:17Z, на SHA e3bf893e3d76b3276a548ee9182f073c901a12e9; эта часть кода не в дельте r2):

  • Матрица decideReconciliation по всем фикстурам AC (cancellation/timeout/guard failure/потерянное событие/applied verdict/running) — 8/8 green на r1, код функции не изменился в дельте r2.
  • Race-guard «перечитать перед записью» (сам факт двойного чтения snapshot/processRuns перед мутацией) — логика не менялась, изменилось только то, что происходит после неё (сама операция записи), что и было предметом High-находки.
  • Идентичность прогона по run-name, трейлеры и класс изменений — не затронуты дельтой.

Вердикт

High из r1 закрыт: показано построчно (таблица выше), тесты и mutation witnesses подтверждают, что фикс действительно закрывает сценарий (комментарий персистентен до фолбл relabel, вторая попытка restore авторитетна, ошибка записи не топит весь снимок тика). Новых находок в дельте не обнаружено. Трейлеры (Issue: #555, User-Visible: no) корректны, changelog не требуется — изменение не видно пользователю продукта.

Зелёный.


Материал раунда

  • Ветка: issue/555-process-reconciler, коммит d0ecd5383155 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 8133acd9b590c43a9bc938d35fef2a570f2c9c84
    git log --all --format='%H %T' | grep 8133acd9b590
    
  • Тело issue: 61feb887bc5d5cae5bac106b48e7f07161e697938ac473cc87c5e47a8ca8d4e6
  • Вердикт конвейера: green · High 0