Files
houseplan-card/docs/reviews/CODE-REVIEW-230-r2.md
T
2026-08-21 14:07:33 +00:00

18 KiB
Raw Blame History

CODE-REVIEW-230-r2

  • Issue: #230
  • Ветка: issue/230-hatch-density-normalization
  • Коммит на ревью: 6836561340886b113aadc66534c28f6bf75dce0f (HEAD)
  • ТЗ: docs/specs/230-hatch-density-normalization.md, ревью ТЗ зелёное на заходе r2 (docs/reviews/SPEC-REVIEW-230-r2.md)
  • Заход код-ревью: r2 · блокирующих циклов израсходовано 1/4 до этого вердикта

0. Раунд r1 — SHA (находка)

Комментарий с вердиктом r1 (2026-08-21T12:33:44Z, «Вердикт: жёлтый · заход r1 · … High: 2 · Medium: 0») не называет SHA, на котором получен разбор — сам документ docs/reviews/CODE-REVIEW-230-r1.md его называет (edf1cca), но комментарий в issue самодостаточным не является. Восстановлено сопоставлением меток времени: edf1cca запушен в 12:11:58 UTC (после конвертации из +03:00), вердикт — 12:33:44 UTC, следующие коммиты (d3468c1, 6811cc0, 6836561) запушены в 13:42–13:49 UTC — то есть строго после вердикта. Подтверждено списком прогонов CI (gh run list): push с headSha=edf1cca (32480753934, 12:12 UTC) идёт непосредственно перед вердиктом, следующий push — уже 6811cc0 (32488390779, 13:44 UTC). SHA r1 = edf1cca8e3651003808627246312a37923a2e181.

Это Low-находка процесса ревью (не продукта): следующий раз называть SHA прямо в первой строке вердикта, а не только в документе.

1. Дельта r1 → r2

git diff edf1cca..HEAD --stat:

docs/reviews/CODE-REVIEW-230-r1.md | 244 ++++++++++++++++++++++++++
scripts/mutation-gate.mjs          |  12 +-
2 files changed, 252 insertions(+), 4 deletions(-)

Только scripts/mutation-gate.mjs — продуктовая (в терминах гейта) правка. Коммит 579d6e5 (документ r1) не в счёт. Три «настоящих» коммита между r1 и r2:

  • d3468c1 — снимает два golden-эталона из edf1cca (ревёрт без переписывания истории).
  • 6811cc0 — возвращает те же два эталона отдельным коммитом с трейлерами Release:/Baseline-Reviewed:.
  • 6836561 — правит guard четырёх юнит-мутантов #230 в scripts/mutation-gate.mjs.

Baseline-файлы (demo/golden/baselines/{baselines-index.json, large-house-zoom-040-dark.png, large-house-zoom-250-dark.png}) в диффе не видны, потому что revert (d3468c1) и повторное принятие (6811cc0) взаимно гасятся — проверено напрямую:

$ git diff edf1cca:demo/golden/baselines/large-house-zoom-040-dark.png \
            6811cc0:demo/golden/baselines/large-house-zoom-040-dark.png   # пусто
$ git diff edf1cca:demo/golden/baselines/large-house-zoom-250-dark.png \
            6811cc0:demo/golden/baselines/large-house-zoom-250-dark.png   # пусто
$ git diff edf1cca:demo/golden/baselines/baselines-index.json \
            6811cc0:demo/golden/baselines/baselines-index.json            # пусто

Т.е. итоговые байты эталонов в 6811cc0 побайтово совпадают с тем, что уже было golden-проверено в CI на edf1cca (job golden: success, прогон 32480753934) — переприкрепление трейлеров не изменило содержимое, только его коммит-обёртку. Делта локальна: не затрагивает src/**, test/wall-thickness.test.mjs, demo/smoke_wall_hatch_density.mjs, документацию — только процессный артефакт (трейлеры коммита) и оснастку мутационного гейта. Полный разбор не требуется по §2.10: это не ребейз, не смена контракта, не новая подсистема, объём несопоставим с исходной задачей.

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

Находка r1 Чем закрыта Где это видно
High-1. Коммит edf1cca меняет demo/golden/baselines/**, но не несёт Release:/Baseline-Reviewed: — гейт provenance красный на этом SHA (прогон 32480753934). d3468c1 снимает эталоны из edf1cca (ревёрт, история не переписана); 6811cc0 возвращает те же эталоны отдельным коммитом с обоими трейлерами. git show -s --format=%B 6811cc0 содержит Release: v1.67.0-beta.1 и Baseline-Reviewed: …/runs/32480753934; node scripts/validate-commit-provenance.mjs --range 579d6e5..6811cc0 — 0 ошибок (проверено мной, см. §3); CI push 32488390779 (headSha=6811cc0) — job provenance: success.
High-2. 4 из 7 мутантов #230 (hatch-step-ignores-cell-cm, -inverted, -unclamped, hatch-density-solid-threshold-off) не пересобирают test-build/ в изолированном worktree — «чистый прогон» падает ERR_MODULE_NOT_FOUND до применения мутации, тест не проверяет ничего. 6836561 меняет guard всех четырёх на npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs && node --test … — тот же паттерн, что у соседних рабочих юнит-мутантов. git diff edf1cca..HEAD -- scripts/mutation-gate.mjs (12 строк, все 4 ID); лично прогнано node scripts/mutation-gate.mjs --id=<каждый из 4> в реальном изолированном git worktree (тот же механизм, что и у эксплуатируемого гейта) — все четыре: поймано 1 из 1 (см. §3).
Low-1. AC7 не перепроверяет отсутствие scale на cell_cm: 25, снята с записью, без правки. Делта её не касается. Не изменялось — наследуется как снятая.

Оба High закрыты содержательно (не только заявлением автора — обе закрывающие правки проверены мной повторным исполнением, см. §3) и процедурно (трейлеры на месте, гейт provenance зелёный, мутанты реально ловят мутацию в том же харнессе, что использует прод-гейт).

3. Как проверялось в r2 — таблица гейтов

Гейт Прогнан Результат
npx tsc --noEmit да чисто
npm test да 1014/1014
npm run build + sha256sum трёх копий бандла да все три идентичны (a6b5f9ec…), git status чист после пересборки
node scripts/mutation-gate.mjs --id=hatch-step-ignores-cell-cm да поймано 1 из 1 (реальный изолированный git worktree, не догадка)
node scripts/mutation-gate.mjs --id=hatch-step-inverted да поймано 1 из 1
node scripts/mutation-gate.mjs --id=hatch-step-unclamped да поймано 1 из 1
node scripts/mutation-gate.mjs --id=hatch-density-solid-threshold-off да поймано 1 из 1
node scripts/mutation-gate.mjs --check (все 80 мутантов реестра, дешёвая проверка применимости патчей) да все ok, ни одного конфликта якорей от правки #230
node scripts/validate-commit-provenance.mjs --range 579d6e5..6811cc0 да 0 ошибок — прямая проверка закрытия High-1
node scripts/validate-commit-provenance.mjs --range origin/dev..HEAD да edf1cca по-прежнему красен индивидуально (ожидаемо, см. §5) — вся остальная история чиста
gh run list / gh run view по SHA веток (живой CI) да edf1cca → provenance: failure, остальные job (frontend, golden, smoke, performance_smoke, process-gate) — success; 6811cc0 и 6836561 → provenance/process-gate: success
node scripts/process-gate.mjs да диапазон origin/dev..HEAD, 9 коммитов, 0 предупреждений
node scripts/check-docs.mjs да чисто (7 файлов, 10 внешних ссылок) — делта документацию не трогает, прогнан для общей гигиены
npm run golden:verify да (сверх минимума) все 82 сцены passed, 0 расхождений — прямое подтверждение, что revert+reapply не изменил ничего рендерящегося
python -m pytest tests_backend -q нет делта не трогает custom_components/**/*.py
demo/smoke_wall_hatch_density.mjs и прочие именованные смоки AC7/8/12 нет делта их не касается (не трогает src/**, сам смок, ни рендереры); r1 уже прогнал и подтвердил OK на этом же коде — наследуется, см. §4
Полный demo/smoke_* (127 шт.) нет делта локальна (оснастка гейта мутаций + перестановка трейлеров), не задета ни одна новая поверхность
performance_smoke нет не в AC, делта не трогает рендер-код
Полный mutation-gate.mjs без --id (~80 мутантов, дорогая пересборка на каждый) нет делта касается только 4 конкретных ID — они прогнаны точечно; --check (дёшево) подтвердил, что остальные патчи не сломаны текстуально

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

Без повторной проверки принято из docs/reviews/CODE-REVIEW-230-r1.md (документ ревью r1, SHA разбора edf1cca8e3651003808627246312a37923a2e181, r1-вердикт от 2026-08-21T12:33:44Z):

  • AC1–AC6, AC9 — численные свойства wallHatchStepUnits / wallHatchNeedsSolid (эталон при cell_cm: 5, физическая пропорция, пределы клампа, откат на дефолт, отсутствие ложного «solid» для тонкой стены). Делта не трогает src/wall-thickness.ts и test/wall-thickness.test.mjs — доказательство не задето.
  • AC7, AC8 — отсутствие scale в patternTransform на эталоне и неизменность паттерна при смене зума (смок wall_hatch_density). Делта не трогает ни смок, ни src/houseplan-card.ts.
  • AC12 — оба рендерера (houseplan-card.ts, space-render.ts) читают шаг из одной функции, побитовое совпадение паттерна. Делта не трогает src/space-render.ts.
  • AC10 (по существу) — 82 golden-сцены без расхождений; в r2 перепрогнано заново (см. §3) и результат совпал, но именно AC10 инвариантен к делте — перепрогон был мерой избыточной осторожности, а не необходимостью.
  • Low-1 — AC7 не перепроверяет отсутствие scale отдельно на cell_cm: 25; снята с записью в r1, делта её не касается, остаётся снятой.
  • Раздел «Что проверено и корректно» r1 целиком (формула, оба рендерера на одной функции, толщина штриха, порог solid через «или», трейлеры на месте в edf1cca, docs/WALL-THICKNESS.md обновлён) — делта не трогает ни один из перечисленных файлов кроме самих коммит-трейлеров, которые в r2 проверены заново (§2, §3).

5. Находки этого раунда

Нет High и Medium. Одна Low, о процессе ревью, не о продукте:

Low-2. Комментарий с вердиктом r1 не называл SHA разбора — пришлось восстанавливать по меткам времени и gh run list (§0). Не блокирует, правки в код не требует; на будущее — называть SHA первой строкой вердикта.

Отдельно зафиксирую вопрос, который не является находкой #230, а подтверждён как заведомо принятое поведение процесса: edf1cca навсегда останется индивидуально «красным» по validate-commit-provenance.mjs, если гонять валидатор на диапазоне origin/dev..HEAD целиком (я проверил — до сих пор краснеет). Это не дефект: .github/workflows/validate.yml гоняет provenance по диапазону этого push (before..head), а не по всей истории ветки (намеренно, комментарий в resolveValidationRange про #165 — иначе переоценивались бы уже закрытые issue), и живой CI подтверждает: push с headSha=edf1cca был красным именно на этом SHA и это уже разошлось по логам CI — история не переписывается по правилу AGENTS.md, факт нарушения зафиксирован навечно, а не скрыт. Автор прозрачно рассказал про это в issue. Дальнейших вопросов к этому нет.

6. Что проверено и корректно (специфично для r2)

  • Оба High из r1 закрыты не только текстом хендоффа, но повторным исполнением: validate-commit-provenance.mjs на трейлерах 6811cc0, живой CI на 6811cc0/6836561, и все 4 мутанта — лично прогнаны через node scripts/mutation-gate.mjs --id= в реальном изолированном git worktree (тот же путь, которым гоняет прод-гейт), а не через «применение патча вручную» — именно ручной способ и был причиной, почему автор не поймал High-2 в первый раз.
  • Байтовое содержимое golden-эталонов в 6811cc0 идентично содержимому, которое уже прошло golden: success в CI на edf1cca — переприкрепление трейлеров не подменило картинку тайком.
  • mutation-gate.mjs --check (все 80 записей реестра) подтверждает, что правка guard-строк не сломала применимость патчей ни для одного мутанта, включая три «смоковых» мутанта #230, которые делта не трогала.
  • Три копии бандла побайтово идентичны после пересборки — делта не затрагивает сборку, но гигиенический прогон подтверждает отсутствие дрейфа.

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

  • Полный demo/smoke_* (127 файлов) — делта не задевает ни одну новую поверхность; именованные смоки AC7/8/12 не перепрогонялись, так как делта их не касается (наследуются из r1, §4).
  • python -m pytest tests_backend — делта не трогает custom_components/**/*.py.
  • performance_smoke — не в AC, делта не трогает путь рендера.
  • Полный node scripts/mutation-gate.mjs без --id (все ~80 мутантов, дорогая пересборка бандла на каждого) — прогнаны точечно только 4 исправленных ID; дёшево проверено --check для остальных 76 (применимость патчей, не исполнение guard).
  • Реестр мутантов #220/#229 (тот же класс дефекта guard, что был в High-2) — автор явно вывел это за скоуп #230 в issue #235; не моя проверка в этом раунде.

8. Вердикт

Зелёный. Оба High закрыты и перепроверены исполнением, а не заявлением. Новых High/Medium делта не создала. Единственная находка раунда (Low-2, про формат вердикта) не блокирует и не требует правки кода.