diff --git a/docs/reviews/CODE-REVIEW-741-r1.md b/docs/reviews/CODE-REVIEW-741-r1.md new file mode 100644 index 00000000..31b2183d --- /dev/null +++ b/docs/reviews/CODE-REVIEW-741-r1.md @@ -0,0 +1,116 @@ +# CODE-REVIEW-741-r1 + +**Issue:** #741 «2.5D: неиспользуемое поле stageSize в IsoOverlayFitEnvelopeInput» +**Материал раунда:** `a8c8f1d68aa7b42a51b67630a0adb69188213459` (ветка `issue/741-drop-stage-size`, поверх `dev@76558bf2`) +**Трек:** `track:show` (поднят с `track:ship` владельцем — риск по участку `iso-scene-render`, #707) +**Заход:** r1 · блокирующих циклов 0/2 + +## Скоуп + +ТЗ (тело issue): удалить неиспользуемое необязательное поле `stageSize` из +`IsoOverlayFitEnvelopeInput` (`src/iso-scene-render.ts`) вместе с обоими местами +передачи (`_isoScene` и обработчик фокуса комнаты, `src/houseplan-card.ts`); +переписать юнит `#725 AC3`, который сравнивал `bounds` при `stageSize: null` и +`{1000, 500}` — после удаления поля сравнение теряет предмет. + +Диф: `src/houseplan-card.ts` (−2 строки), `src/iso-scene-render.ts` (−1 строка +типа), `test/iso-scene-render.test.mjs` (тест переписан). Класс A (`src/**`) + +класс B (тест). Один коммит, трейлеры `Issue: #741` / `User-Visible: no` — +верно: изменение не видимо пользователю, changelog не тронут и не должен быть. + +## Как проверялось + +| Гейт | Статус | Как | +|---|---|---| +| `tsc --noEmit` | ✅ подтверждён | Validate на `a8c8f1d6`: https://github.com/Matysh/houseplan-card/actions/runs/36821143516 — не перегонял отдельно | +| `npm test` (3410 тестов) | ✅ сам перегнал | `npm test` → `pass 3431, fail 0, skipped 1`; дополнительно `node --test test/iso-scene-render.test.mjs` отдельно → `31/31 pass` | +| `npm run build` + сверка бандла | ✅ подтверждён Validate | не перегонял — дешёвый гейт уже зелёный на этом SHA | +| «Тест умеет падать» (`#725 AC3/#741`) | ✅ проверено исполнением | временно мутировал `resolveIsoOverlayFitEnvelope` (внёс в `base` зависимость от `targetView`/aspect, имитируя регрессию «stage просочился в bounds»); пересобрал `test-build`, прогнал файл — упало 2 из 31 (`the overlay fit bounds depend only on scene.frame…`, `the view fits the bounds`); откатил файл, пересобрал, снова 31/31. Тест чувствителен к ровно тому регрессу, который он называет | +| `node scripts/smoke-select.mjs --base 76558bf2 --head a8c8f1d6` | выполнено | **НЕОПРЕДЕЛЁННОСТЬ**: 0 символов проекта на изменённых строках (чистое удаление мёртвого поля типа), ни один смок не связан доказуемо. Автор дополнительно прогнал `smoke_room_fit`, `smoke_isometric_contract`, `smoke_iso_flat_parity` (все три существуют в `demo/`, по названию относятся к затронутой функции) — разумный выбор поверх визуального минимума; браузерные смоки сам не перегонял (AC их не называет, Chromium не поднимал) | +| `npm run golden:verify` | не требуется | метки `ci:golden` нет, рендер пикселей не меняется (ниже) | +| `pytest tests_backend` | не требуется | Python не тронут | +| `npm run invariants` | не требуется | геометрия и ссылки на неё не меняются — меняется только форма входного объекта функции разметки, не геометрическая модель | + +## Риск по изменённым участкам (#707) + +- **perf — `src/iso-scene-render.ts:651` (удалена), участок `iso-scene-render`.** + Поведение уже зафиксировано: читал тело `resolveIsoOverlayFitEnvelope` + (`src/iso-scene-render.ts:658-668`) и в версии до диффа, и после — `input.stageSize` + нигде не читается, функция использует только `baseBounds`, `entries`, + `ownerId`, `targetView`. Удаление поля из интерфейса и обоих мест передачи — + изменение только по типам, ни одного пути исполнения не задевает. Документ + `docs/ISOMETRIC.md` (раздел «Fit», строки ~245-263) фиксирует контракт прямо: + «Neither zoom nor a stage resize is a layout event… the fit envelope reserves + no nudge budget» — т.е. stage/aspect и до этой задачи не должны были влиять на + раскладку. route: `fix` (критерий не нарушен — `perf-touch`: изменения + производительности нет, т.к. убираемый путь не исполнялся). +- **visual (render) — `src/iso-scene-render.ts:651` (удалена), участок `iso-*` (render).** + То же: поле не читалось, значит удаление не может изменить картинку. Плюс + переписанный юнит `#725 AC3/#741` это утверждает явно и проверяемо: `bounds` + идентичны для пяти разных aspect (0.5, 1, 1000/500, 320/180, собственный aspect + кадра), различается только `view`; я подтвердил мутационным прогоном (таблица + выше), что тест ловит регресс, где это переставало бы быть так. Визуальный + минимум прогоняется `gate:small` (автор) — отдельного `ci:golden` для + переноса/удаления неиспользуемого поля типа не требуется: рендер-путь не + меняется ни на один пиксель, доказательство — отсутствие чтения поля в коде + плюс тест. route: `fix`. + +## Находки + +Нет. Диф — корректное удаление мёртвого поля, оба места передачи вычищены +полностью (`grep -rn stageSize src/` — пусто), заменяющий тест по существу +эквивалентен старому AC и доказуемо чувствителен к регрессу (проверено +мутацией, не только чтением). Якорь мутационного реестра `#725 AC5` +(`scripts/mutation-registry.mjs:13846-13857`, строка +`const aspect = scene.frame.w / scene.frame.h; // #713/#725: …`) не тронут +диффом — заявление автора «якорь #725 на месте» подтверждено чтением. + +Побочный файл `test/iso-overlay-fixture-types.test.mjs` тоже содержит +`DEAD_SCENE_FIELDS` со строкой `'stageSize'` — проверил: это другой тип, +`OverlaySceneFixture` входа `buildIsoOverlayRenderScene`/`overlayScene` +(#714/#724/#732), не `IsoOverlayFitEnvelopeInput`. Задача #741 его не задевает +и трогать не должна была. + +## Что проверено и корректно + +- Оба места передачи `stageSize` удалены полностью, без мёртвых остатков + (переменная `stage` в обработчике фокуса комнаты продолжает использоваться + для `baseFit`/`targetOf` — не осиротела). +- Трейлеры коммита корректны: `Issue: #741`, `User-Visible: no`, changelog не + тронут — согласуется (невидимое изменение). +- Диф в пределах заявленных ship-лимитов по `src/**` (+2/−4), новых файлов, + i18n, полей конфига нет — но ревью идёт по треку `show`, как и требует метка. +- `npm test` зелёный целиком (3431 pass / 0 fail / 1 skip), не только + затронутый файл. + +## Чего не проверял + +- Полный `tsc --noEmit` и `npm run build` с сверкой трёх копий бандла — + перегонять не стал, взял зелёный Validate на этом же SHA (дешёвые гейты + параграф §8). +- Браузерные смоки (`smoke_room_fit`, `smoke_isometric_contract`, + `smoke_iso_flat_parity`) сам не выполнял — `smoke-select` вернул + НЕОПРЕДЕЛЁННОСТЬ (связь не доказана), AC их не называет, Chromium не + поднимал; полагаюсь на заявленный автором зелёный прогон. +- `npm run golden:verify`, `pytest tests_backend`, `npm run invariants` — + не запускал, т.к. не применимы к этому диффу (см. таблицу гейтов). + +## Вердикт + +Зелёный. AC выполнены полностью, класс риска `perf`/`visual` закрыт документом +`docs/ISOMETRIC.md` и подтверждён чтением кода и мутационной проверкой теста; +находок нет. + +--- + + + +## Материал раунда + +- Ветка: `issue/741-drop-stage-size`, коммит `a8c8f1d68aa7` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `84476d1a976435349921933a39178f4d2989d75f` + ``` + git log --all --format='%H %T' | grep 84476d1a9764 + ``` +- Тело issue: `2ca2e0ad99b76c735fed07cd61d7ee08f1a58d6a9a6365322540273eb3d0c794` +- Вердикт конвейера: `green` · High 0 · маршрут `fix`