mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 04:38:55 +00:00
193 lines
18 KiB
Markdown
193 lines
18 KiB
Markdown
# CODE-REVIEW-230-r2
|
||
|
||
- Issue: [#230](https://github.com/Matysh/houseplan-card/issues/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, про формат
|
||
вердикта) не блокирует и не требует правки кода.
|