# CODE-REVIEW-400-r2 - Issue: https://github.com/Matysh/houseplan-card/issues/400 - ТЗ: `docs/specs/400-beta-polish.md` (r1), ревью ТЗ зелёное (комментарий 2026-08-31T04:44:56Z) - Ревью r1: `docs/reviews/CODE-REVIEW-400-r1.md`, вердикт жёлтый, SHA `73ecfbd1d74a6d194f62e8e83766294018f8eb81`, опубликовано коммитом `f07e3c54` (2026-08-31T05:06:35Z) - Диапазон материала r2: `origin/dev...HEAD`, HEAD на момент разбора и на момент вердикта — `f07e3c54ffea88b0d77b12065bf318fc820d0ebf` - Заход r2, блокирующих циклов израсходовано **1 из 4** до этого вердикта ## Дельта раунда (§2.10) Предыдущий вердикт получен на SHA `73ecfbd1`. Дельта: ``` $ git diff 73ecfbd1..HEAD --stat docs/reviews/CODE-REVIEW-400-r1.md | 196 +++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) ``` Это **единственный** коммит между r1 и r2 — публикация документа r1-ревью самим конвейером. `git diff 73ecfbd1..HEAD -- src/ scripts/ demo/` пуст. Ни один файл кода, теста или мутанта не изменился с момента r1. Однако в issue после публикации r1 есть комментарий автора (2026-08-31T05:14:07Z, **позже** документа r1 на 5:06:35Z): *«Medium закрыт, HEAD обновлён»*, описывающий конкретную правку — именованную константу `HANDLE_PAINT_ORDER = ['edges', 'corners']`, изменённый патч мутанта, замену `handles[3]` на выбор ручки по роли в `smoke_furniture`. Это и есть предмет разбора r2: делает ли фактический репозиторий то, что утверждает комментарий. **Ответ: нет.** Ни одна из трёх заявленных правок не существует ни в одном коммите ни одной ветки: ``` $ git grep -l "HANDLE_PAINT_ORDER" $(git rev-list --all) -- src/ (пусто — по всей истории репозитория) $ for b in $(git branch -a --format='%(refname)' | grep issue/400); do git log $b --oneline -3; done f07e3c54 docs: review document for #400 73ecfbd1 build: refresh bundle trees and the doc capture for #400 fe3b85c0 fix: the corner handle wins on small furniture, guides exclude the real drag (#400) (других веток с #400 нет) $ grep -n "handles\[3\]" demo/smoke_furniture.mjs 360: const se = handles[3] || stageEl(); // SE — четвёртый угол ``` `git fetch origin issue/400-beta-polish` не принёс новых коммитов — `HEAD` на удалённой ветке совпадает с локальным. Единственное отдалённо связанное действие после комментария — коммит `f07e3c54`, но это публикация r1-ревью, а не фикс. ## Закрытие раунда r1 | Находка r1 | Чем закрыта | Где это видно | |---|---|---| | **M1** — мутант `furniture-edge-handles-steal-the-corner` правит только текст JS-комментария, не порядок отрисовки; регрессию AC1 не ловит | **Не закрыта.** Автор заявил фикс в комментарии, но код не изменился | `git diff 73ecfbd1..HEAD -- src/ scripts/` пуст; повторный прогон ниже даёт тот же результат, что в r1 | Повторный прогон того же гейта, что дал находку в r1: ``` $ node scripts/mutation-gate.mjs --id=furniture-edge-handles-steal-the-corner ok чистый прогон: node demo/smoke_furniture_polish.mjs FAIL furniture-edge-handles-steal-the-corner: тест остался зелёным на сломанном коде поймано 0 из 1 ``` Идентично записи в CODE-REVIEW-400-r1.md. Патч мутанта в `scripts/mutation-gate.mjs:756-759` по-прежнему меняет только строку внутри `/* ... */`. ## Унаследовано из r1 Всё, что предметно не связано с M1, дельта не задевает (дельта нулевая), поэтому принимается без повторной проверки по документу `docs/reviews/CODE-REVIEW-400-r1.md`, SHA `73ecfbd1`: - AC1/AC2 (угловая ручка выигрывает хит на мелкой мебели) — доказаны живым браузерным смоком `smoke_furniture_polish.mjs` (`smallFurnitureCornerWinsTheHit: true`, `largeFurnitureKeepsBothHandles: true`), прогнано вживую в r1. - AC4/AC5 (`_alignCandidates` исключает по `_deviceDrag`) — доказаны смоком `smoke_align_guides.mjs` и вторым мутантом (`align-guides-exclude-dead-source`, «поймано 1 из 1»); перепрогнан в r2 с тем же результатом (см. таблицу гейтов). - AC3/AC6 (решение по 38 help-строкам в `docs/ARCHITECTURE.md`, бюджет 285 425 / 300 000) — подтверждены в r1 чтением записи и прогоном `bundle:budget`; в r2 бюджет перепрогнан и даёт то же число (см. ниже) — код не менялся, значит и вывод не мог измениться. - Трейлеры четырёх содержательных коммитов (`60c211c8`, `532743b0`, `fe3b85c0`, `73ecfbd1`) — проверены в r1, дельта их не касается. - Смок-выборка `scripts/smoke-select.mjs --base origin/dev --head HEAD` и 9 зелёных прямых совпадений — не перезапускались в r2: набор файлов, который они покрывают, не изменился (дельта r2 — один docs-файл). ## Как проверялось (r2) Дешёвые гейты гоняются в каждом раунде независимо от локальности дельты (§2.10). Диапазон src/scripts не менялся, но числа сверены заново, а не унаследованы вслепую. | Гейт | Команда | Результат | |---|---|---| | typecheck | `npx tsc --noEmit` | чисто | | unit | `npm test` | 1668 tests, 1667 pass, 0 fail, 1 skipped — совпадает с r1 | | build | `npm run build` | успешно, 15.6s | | сверка трёх копий бандла | `npm run bundle:sync` | `git status --porcelain` пуст до и после — `dist`, `custom_components/houseplan/frontend`, `demo/srv/assets` уже синхронизированы, дрейфа нет | | бюджет | `npm run bundle:budget` | initial 285 425 / 300 000, тот же варнинг про запас < 15 000 Б (#367, фоновый, не следствие этой задачи) — число идентично r1 | | мутант (предмет M1) | `node scripts/mutation-gate.mjs --id=furniture-edge-handles-steal-the-corner` | **красный, как в r1**: поймано 0 из 1 | | мутант (контроль) | `node scripts/mutation-gate.mjs --id=align-guides-exclude-dead-source` | зелёный: поймано 1 из 1, без изменений | Не прогонялось (дельта — один docs-файл, ничего из перечисленного её не задевает и было закрыто в r1): `check-docs.mjs` (diff r2 не трогает `src/**`), `no-new-any.mjs` (нет новых строк кода), `smoke-select.mjs` (набор символов не менялся), `model-invariants.mjs` (diff не про геометрию), `golden:verify`, `pytest tests_backend`, performance-профили, полный набор смоков. ## Находки ### Medium (в скоупе задачи — по-прежнему открыта из r1) **M1 (перенесена из r1, не закрыта).** Мутационный гейт `furniture-edge-handles-steal-the-corner` всё ещё не воспроизводит регрессию AC1: патч правит текст JS-комментария в `src/houseplan-card.ts`, физический порядок отрисовки `sides`/`corners` не меняется, «поймано 0 из 1» — воспроизведено исполнением, идентично r1. Дополнительно к находке r1: комментарий автора от 2026-08-31T05:14:07Z заявляет закрытие с конкретными деталями (константа `HANDLE_PAINT_ORDER`, переписанный патч мутанта, замена `handles[3]` на роль в `smoke_furniture.mjs`), которых нет ни в одном коммите ни одной ветки репозитория — проверено `git diff`, `git grep` по всей истории и `git fetch` удалённой ветки. Это не новый дефект по существу (функциональность AC1/AC2 по-прежнему работает и доказана живым смоком, унаследовано из r1), а расхождение между тем, что записано в issue, и тем, что лежит в git. Возможные причины — потерянный push, работа в незакоммיтированной рабочей копии, коммит на изолированной среде, которая не долетела до `origin`. Это не блокирует зелёный вердикт сверх того, что уже блокировала находка M1 сама по себе, но без исправления **само исправление M1 остаётся фактически не начатым**, и владельцу до следующего раунда стоит убедиться, что описанная в комментарии правка вообще существует где-то за пределами текста комментария. **Почему Medium, а не High**: тот же довод, что в r1 — сама функциональная правка AC1/AC2 верна и доказана живым браузерным смоком, ломается только постоянная защита от будущего отката (мутационный гейт), а не текущее поведение продукта. **Ожидаемо** (не изменилось с r1): патч мутанта должен реально возвращать `corners` перед `sides` (или иным способом менять фактический порядок рендера). Проверка приёмки: `node scripts/mutation-gate.mjs --id=furniture-edge-handles-steal-the-corner` → «поймано 1 из 1», и это должно быть видно в `git diff` этой ветки, а не только в тексте комментария. ## Что проверено и корректно - Все AC, кроме предмета M1, — без изменений с r1, унаследовано с указанием источника выше. - Дешёвые гейты r2 воспроизводят те же числа, что в r1 (tsc чист, 1667/0 тестов, бюджет 285 425/300 000, три копии бандла синхронизированы) — ожидаемо, так как дельта нулевая по коду. - Контрольный мутант `align-guides-exclude-dead-source` по-прежнему ловит свою регрессию (1 из 1) — AC4/AC5 не деградировали. - Ветка `issue/400-beta-polish` — единственная с материалом по #400; других мест, где могла бы жить заявленная правка, не найдено. ## Чего не проверял - Не искал вне git (например, в артефактах CI-запусков или локальных стейджах автора) заявленную правку — ревью материалом считает только репозиторий, как предписано; если правка существует где-то ещё, её нужно закоммитить и запушить, тогда она попадёт в следующую дельту. - Не прогонял `check-docs.mjs`, `no-new-any.mjs`, `smoke-select.mjs`, `model-invariants.mjs`, `golden:verify`, `pytest tests_backend`, performance-профили и полный набор смоков — дельта r2 их не касается (единственный файл дельты — docs), и они были закрыты в r1 на том же коде.