18 KiB
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, про формат вердикта) не блокирует и не требует правки кода.