# CODE-REVIEW-329-r3 Этап: code · заход r3 · блокирующих циклов израсходовано 2 из 4 ## Скоуп раунда Предыдущий вердикт (code r2, `226`-й комментарий issue) — красный, единственная находка **r2-H1**: мутант `junction-limit-backend-raw-baseline` в `scripts/mutation-gate.mjs` был добавлен с `guard: 'python3 -m pytest tests_backend/test_junction_limits.py -q'`, в обход конвенции ВСЕХ прочих бэкенд-мутантов реестра (`node scripts/backend-test-guard.mjs `), из-за чего `test/mutation-gate.test.mjs` красился на CI. Вердикт r2 получен на SHA `26c6e870` (Matysh: «Актуальный HEAD ветки для ревью — `26c6e870`», непосредственно перед вердиктом). Автор закрыл находку коммитом `273b0d5e`, текущий HEAD. Дельта этого раунда: ``` git diff 26c6e870..HEAD --stat (без docs/reviews/*) scripts/mutation-gate.mjs | 4 ++-- test/mutation-gate.test.mjs | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) ``` Дельта не затрагивает `src/**`, бэкенд-продакшен-код, геометрию, документацию пользователя — только реестр мутантов и его контрактный тест. Это ровно то, что запрошено находкой r2-H1, без побочных правок. Разбор по дельте признан достаточным: изменение локально, не является ребейзом, не меняет контракт поведения продукта и по объёму несопоставимо мало относительно исходной задачи (две строки правки guard + возврат теста к исходному виду). ## Что проверено (дельта) 1. **Форма guard соответствует конвенции.** Новый guard — `node scripts/backend-test-guard.mjs test_legacy_baseline_is_judged_after_the_same_migration tests_backend/test_junction_limits.py` — совпадает по форме со всеми остальными записями реестра, использующими бэкенд-хелпер (проверено `grep -n "backend-test-guard" scripts/mutation-gate.mjs` — 16 вхождений, новое неотличимо от прочих). 2. **Контракт теста возвращён к исходному виду.** `test/mutation-gate.test.mjs` снова требует, чтобы guard называл `.mjs` (убрана временная ветка `.py`, убран комментарий-объяснение, который её оправдывал). `grep` подтвердил: других мутантов, называющих `.py` напрямую (в обход хелпера), в реестре нет — правка не откроет дыру для будущих аналогичных случаев. 3. **Мутант по-прежнему ловит регрессию.** Прогнан НЕ ТОЛЬКО `--check` (проверяет только уникальность якоря патча), а полный цикл конкретного мутанта: `node scripts/mutation-gate.mjs --id=junction-limit-backend-raw-baseline` → ``` ok чистый прогон: node scripts/backend-test-guard.mjs test_legacy_baseline_is_judged_after_the_same_migration tests_backend/test_junction_limits.py ok junction-limit-backend-raw-baseline: тест покраснел, как обязан поймано 1 из 1 ``` То есть подтверждено обеими половинами: код без мутации проходит целевой тест, а с применённым патчем (`migrated = config` вместо прогона через `commit_wall_segment_model`) целевой тест красится. Это и есть доказательство «тест умеет падать» для данной находки. 4. **pytest напрямую.** `python3 -m pytest tests_backend/test_junction_limits.py -q` → `8 passed`. (pytest в песочнице ревью отсутствовал изначально, установлен `pip3 install --user pytest` — тот же шаг, что r2 констатировал как разовую особенность окружения, не продукта.) 5. **`npm test`** → `# tests 1391 / # pass 1390 / # fail 0 / # skipped 1`, совпадает с итогом, который автор указал в коммите `273b0d5e`. 6. **`npx tsc --noEmit`** — чисто (дельта не трогает `.ts`, но прогнан по правилу «всегда»). 7. **`npm run build`** — собрался, `dist/houseplan-card.js` создан. Сверка бандла: `cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` — совпадает побайтово. Третья копия, `demo/srv/assets/houseplan-card.js`, в дереве отсутствует (она untracked рабочая копия demo-стенда, `bundle-sync.mjs` создаёт её по требованию) — отсутствие ожидаемо и не связано с этой правкой, дельта фронтенд не трогает. 8. **`node scripts/check-docs.mjs`** → `Documentation checks passed (7 files, 10 external links)`. Формально необязателен (дельта не в `src/**`), прогнан по инерции — зелёный. 9. **`node scripts/smoke-select.mjs --base 26c6e870 --head HEAD`** → «Исполняемого frontend-диффа нет (`src/**/*.ts` не тронут). Browser-smoke этим диффом не выбираются — выбирать нечего». Полный прогон смоков не делал — инструмент прямо говорит, что выбирать нечего, а не «неопределённость». ## Закрытие раунда r2 | Находка | Чем закрыта | Где это видно | |---|---|---| | r2-H1: guard бэкенд-мутанта в обход конвенции `.mjs`-хелпера, контрактный тест красный на CI | `scripts/mutation-gate.mjs`: guard мутанта `junction-limit-backend-raw-baseline` переписан на `node scripts/backend-test-guard.mjs test_legacy_baseline_is_judged_after_the_same_migration tests_backend/test_junction_limits.py`; `test/mutation-gate.test.mjs`: контракт `every guard command points at a file that exists` возвращён к проверке только `.mjs`, временная ветка для `.py` и оправдывающий её комментарий убраны | Коммит `273b0d5e`; `npm test` 1390/0 (было 1389 passed/1 failed на `0824e510`); точечный прогон `node scripts/mutation-gate.mjs --id=junction-limit-backend-raw-baseline` — «поймано 1 из 1» | ## Унаследовано из r2 (и, транзитивно, из r1) Без повторной проверки исполнением приняты — дельта r3 их не касается кодом, которого они доказывают: - **H1 (бэкенд мигрирует обе стороны перед сравнением)** — доказано в r1, подтверждено закрытым в r2 тестом `test_legacy_baseline_is_judged_after_the_same_migration` и мутантом. Документ: `docs/reviews/CODE-REVIEW-329-r2.md`, раздел «Закрытие раунда r1», SHA `26c6e870`. - **M1 (мёртвый код `apexCaps`/`clipPolygonOutsideCap`/`degenerateApexCaps` удалён)** — `docs/reviews/CODE-REVIEW-329-r2.md`, SHA `26c6e870`. - **M2/M3 (дубль раздела USER-GUIDE устранён, канал Resize описан верно)** — там же, SHA `26c6e870`. - **M4 (AC10, Optimize не создаёт новых нарушений, доказан двумя юнитами)** — там же, SHA `26c6e870`. - **Остальные AC (П1–П5 юниты и границы, §4 честная вершина + golden, AC5a/b, AC7a через реальный pointer-жест, AC7b, паритет AC9, скоуп применения AC8)** — впервые проверены и признаны корректными в `docs/reviews/CODE-REVIEW-329-r1.md`, подтверждены неизменными в r2 (дельта r1→r2 их не трогала за вычетом перечисленных выше M1–M4 и H1); дельта r2→r3 тем более их не трогает (единственные изменённые файлы — `scripts/mutation-gate.mjs` и `test/mutation-gate.test.mjs`, не пересекающиеся с производственным кодом junction-limits, рендером или бэкендом). Документ: `docs/reviews/CODE-REVIEW-329-r1.md`, SHA `e1df015d`; golden/invariants/мутанты для этих AC — из отчёта r1 («что прогнано»). - **Полный HA-harness backend не прогоняется** — унаследованный пробел окружения, констатирован ещё в r1 (недоступны `homeassistant`/`voluptuous`), подтверждён в r2, в r3 не переоценивался — дельта его не касается. ## Чего не проверял в этом раунде и почему - **`npm run golden:verify`** — не прогонял. Дельта не трогает рендер, геометрию, стили, слои; полная матрица (~80 сцен) уже прогнана и зелёная в r2 на коде, который в r3 не менялся. - **`npm run invariants`** — не прогонял. Дельта не трогает рёбра комнат, записи толщины, `layout`, `marker.space`, `open_spans` — инварианты модели здесь неприменимы. - **Браузерные смоки** — не прогонял ни одного. `smoke-select.mjs` для диапазона `26c6e870..HEAD` прямо сообщает «выбирать нечего»: единственные изменённые файлы — `scripts/mutation-gate.mjs` и `test/mutation-gate.test.mjs`, оба вне `src/**`, ни один смок такой диапазон не оценивает даже как слабую связь. - **`python -m pytest tests_backend -q` (полный набор)** — прогнал только файл `test_junction_limits.py`, целевой для находки; полный бэкенд-набор не запускал, так как дельта не трогает бэкенд-продакшен-код (только тест и реестр мутантов на фронтенде/CI), а `test_junction_limits.py` — тот файл, контракт которого проверяет закрытие r2-H1. - **Performance-профили** — не прогонял, в AC не названы и дельта их не задевает. - **Полный `.github/workflows/mutation-gate.yml` (пересборка бандла на каждого мутанта)** — не прогонял; это предрелизный гейт (PROCESS.md §8), несоразмерен точечной правке одной записи реестра. Точечный прогон именно этого мутанта сделан отдельно (см. выше) и достаточен как доказательство. ## Вывод Единственная находка r2 (High) закрыта по существу и подтверждена исполнением: guard мутанта следует конвенции, контракт реестра не ослаблен, мутант по-прежнему доказанно ловит регрессию H1 (1/1), `npm test` и `pytest tests_backend/test_junction_limits.py` зелёные. Новых находок delta не создаёт — правка изолирована в тестовой инфраструктуре, не пересекается с продакшен-кодом или пользовательским поведением. Trailer коммита (`Issue: #329`, `User-Visible: no`) корректен для чисто тестового изменения; changelog не тронут — верно, правок не требовалось. Вердикт: зелёный.