17 KiB
CODE-REVIEW-329-r2
Issue: #329 — экстремально острая вершина комнаты рендерится «трезубцем»; ограничения на стыки стен.
Заход: r2. Предыдущий цикл (r1) — вердикт красный, High: 1, Medium: 4, документ
docs/reviews/CODE-REVIEW-329-r1.md, получен на SHA e1df015d (коммит «docs: review document for #329»,
после которого автор добавил четыре коммита закрытия находок).
Скоуп разбора (§2.9)
Дельта r1→r2 — 4 коммита, 12 файлов, +301/-191:
0824e510 test: prove AC10 — Optimize adds no junction violation (#329 M4)
574fea98 docs: one limits section, and Resize does raise a toast (#329 M2, M3)
8f0b6b97 refactor: drop the chamfer helpers the honest apex made dead (#329 M1)
0bb42cae fix: the backend judges both sides after the same migration (#329 H1)
Файлы: custom_components/houseplan/junction_limits.py, src/wall-thickness.ts,
test/junction-limits.test.mjs, tests_backend/test_junction_limits.py,
scripts/mutation-gate.mjs, docs/CONFIG-COMPATIBILITY.md, docs/USER-GUIDE.md,
docs/USER-GUIDE.ru.md, docs/specs/329-junction-limits.md,
docs/images/screenshots.json, dist/houseplan-card.js,
custom_components/houseplan/frontend/houseplan-card.js.
Каждый коммит — прямое закрытие одной находки r1 (H1, M1, M2+M3, M4), rebase на
ушедший вперёд dev не производился (дельта считается от r1-документа, ветка не
двигалась), новая подсистема не задета, поведенческий контракт не меняется — он
чинится до заявленного. Разбор ограничен дельтой плюс всем, до чего она
дотягивается (backend-паритет AC9, AC10, §4/AC6 честная вершина — см. ниже).
П1–П5 как таковые (AC1–AC5), AC7a/AC7b, AC8 дельтой не задеты — унаследованы.
Как проверялось
Прогнано (SHA 0824e510):
npx tsc --noEmit— чисто.npm test— 1389 passed, 1 failed, 1 skipped (см. находку r2-H1 ниже). Функциональные тесты этой дельты, включая новые AC10-юниты (ok 493,ok 494) и переписанные бэкенд-паритетные фикстуры, — все зелёные; падает отдельный метатест реестра мутантов.npm run build+ сверка бандлов:dist/houseplan-card.js==custom_components/houseplan/frontend/houseplan-card.js(байт-в-байт), пересборка с нуля идентична закоммиченномуdist(diff пуст).node scripts/check-docs.mjs— «Documentation checks passed (7 files, 10 external links)» — отпечатокdocs/images/screenshots.json(единственный файл дельты вdocs/, пересчитанный после правкиsrc/wall-thickness.ts) валиден.python3 -m pytest tests_backend/test_junction_limits.py -q— 8 passed (окружение не неслоpytestизначально; установлен локальноpip3 install pytest pytest-asyncio, что и позволило прогнать гейт, а не просто прочитать код). Дисциплина «тест умеет падать»: вручную откатил патч мутантаjunction-limit-backend-raw-baseline(migrated, _ = commit_wall_segment_model(config)→migrated = config) — 2 из 8 тестов падают (test_legacy_baseline_is_judged_after_the_same_migration,test_a_write_that_adds_a_violation_is_refused_with_a_stable_code), затем восстановил файл — тест 8/8 подтверждён на исходном коде.npm run bundle:sync && npm run golden:verify— вся матрица (~80 сцен, включаяsharp-apex-legacy-darkиjunction-owner-repro-dark) — зелёная, 0 отказов. Нужен был, поскольку M1 правит геометрический файлwall-thickness.ts; удаление мёртвого кода подтверждено эмпирически — ни один кадр не сдвинулся.node scripts/mutation-gate.mjs --check— реестр анкеров цел (совпадает с прошедшим юнитом «every mutant patch anchors exactly once»,ok 688).node scripts/smoke-select.mjs --base e1df015d --head HEAD— «дифф исполняемый, но ни один смок не связан доказуемо»; единственный символ дельты вsrc/**, которого нет в смоках, —DEGENERATE_APEX_MAX_DEGREES, но дельта его не меняет (значение и семантика те же, переставлен только JSDoc-комментарий при удалении соседних мёртвых функций). Решение: смоки не гоняю — это чистое перемещение комментария, а не новый контракт; поведенческую часть уже покрываетgolden:verify(зелёный целиком).
Не прогонялось: полный HA-harness (pytest tests_backend целиком падает при
сборе — ModuleNotFoundError: homeassistant, voluptuous; тот же пробел был и в
r1, дельта его не устраняет и не обязана). Полная матрица браузерных смоков и
мутантов — дельта не даёт для этого повода (см. вывод smoke-select.mjs выше);
она предрелизный гейт, не гейт ревью.
Находка r2 (High, в скоупе — блокирует)
r2-H1. Новый мутант ломает npm test, то есть красный CI-гейт validate.yml
scripts/mutation-gate.mjs (коммит 0bb42cae, закрытие H1) добавляет запись
junction-limit-backend-raw-baseline с
guard: 'python3 -m pytest tests_backend/test_junction_limits.py -q',
test/mutation-gate.test.mjs:33 («every guard command points at a file that
exists») требует, чтобы guard называл .mjs-файл — конвенция, которой следуют
ВСЕ остальные бэкенд-мутанты в этом же реестре (node scripts/backend-test-guard.mjs <pattern> [<file>], см. строки 1061, 1073, 1099,
1156, 1223, 1541, 1766, 1777, 1788, 1799, 1810, 1856, 3025, 3037, 3049). Новая
запись — сырой python3 -m pytest ... без .mjs, и .split(' ').find(part => part.endsWith('.mjs')) возвращает undefined.
Воспроизведено исполнением: npm test на HEAD (0824e510) —
# fail 1, тест 689 падает с
junction-limit-backend-raw-baseline: guard не называет исполняемый файл.
Проверено дважды (npm test целиком и точечно node --test test/mutation-gate.test.mjs) — не флейк. .github/workflows/validate.yml:282
гоняет именно npm test, значит CI на этой ветке красный по этой же причине.
Функционально мутант работает и семантически осмыслен (я вручную применил его
патч и подтвердил, что он ловит регресс H1 — 2/8 тестов падают, откат правки
восстанавливает 8/8); дефект чисто в форме guard, не в сути защиты. Это не
довод в пользу «оставить как есть»: npm test — обязательный дешёвый гейт этого
самого ревью, и он красный на проверяемом SHA.
Нужно: привести guard к конвенции реестра, например
node scripts/backend-test-guard.mjs test_legacy_baseline_is_judged_after_the_same_migration tests_backend/test_junction_limits.py
(или обеими новыми тестами через -k "test_legacy_baseline_is_judged_after_the_same_migration or test_a_write_that_adds_a_violation_is_refused_with_a_stable_code",
если нужно ловить оба). Однострочная правка, но npm test должен быть зелёным на
SHA, уходящем в dev.
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где видно |
|---|---|---|
| H1 (High) — бэкенд судит легаси-базу сырой, отклоняет несвязанную правку | 0bb42cae: validate_junction_limits гоняет и previous, и candidate через новую _migrated_spaces() → commit_wall_segment_model до подсчёта |
junction_limits.py (диф выше); тест test_legacy_baseline_is_judged_after_the_same_migration — 8/8 pytest; мутант junction-limit-backend-raw-baseline вручную подтверждён (2/8 падают без фикса) |
M1 — мёртвый код apexCaps/clipPolygonOutsideCap/degenerateApexCaps |
8f0b6b97: поле интерфейса и обе функции удалены целиком, JSDoc над wallBodiesGeometry возвращён на место |
grep -rn по всему репо (кроме текста самого r1-документа) — не находит; golden:verify вся матрица зелёная — поведение не изменилось |
| M2 — раздел «Ограничения стыков стен» задвоен в RU-гайде | 574fea98 |
docs/USER-GUIDE.ru.md — ровно один заголовок ### Ограничения стыков стен (строка 420) |
| M3 — гайд (RU+EN) описывает Resize как «просто останавливается», хотя тост есть | 574fea98: текст переписан — «…и один раз за жест поясняет тостом, какое правило дальше нарушится» |
docs/USER-GUIDE.md/.ru.md (диф выше); код _rszMove (src/houseplan-card.ts:9183-9191, resize.limit_stopped + _junctionLimitLabel) не менялся — он и раньше показывал тост, теперь описан верно |
| M4 — AC10 (Optimize не добавляет нарушений) не доказан | 0824e510: два юнит-теста в test/junction-limits.test.mjs, считающие нарушения тем же способом, что барьер записи (обе стороны через commitWallSegmentModel/commit_wall_segment_model) |
npm test — ok 493, ok 494; первый тест на фикстуре владельца, второй — граничный случай П4 (узлы ровно в 5 см), явно проверяет, что база несёт нарушение (не доказывает пустоту) |
Все пять находок r1 закрыты по существу, с наблюдаемым следом в коде/тестах, а не только заявлением автора. Ни одна не переоткрыта.
Унаследовано из r1
Не тронуто дельтой r1→r2, повторно не проверялось — принято по документу
docs/reviews/CODE-REVIEW-329-r1.md, SHA e1df015d:
- AC1–AC5 (П1–П5 как юнит-предикаты с границами) —
test/junction-limits.test.mjsдо строки 216 дельтой не изменён (только дополнен снизу тестами AC10). - §4/AC6 (честная вершина легаси-апекса + golden-сцена
sharp-apex-legacy-dark) — логика самого предиката (isDegenerateApexCorner) не менялась, только удалён неиспользуемый код вокруг неё; повторно подтверждено попутно этим раундом черезgolden:verify(зелёный), но отдельного разбора алгоритма не требовалось. - AC7a/AC7b (Resize/«Толщина»: смок через настоящий pointer-жест, байт- неизменность плана, текст объяснения/тоста) — код и смоки не в дельте; M3 правил только прозу гайда, не контракт и не тест.
- AC8 (скоуп применения — какие поверхности проверяются) — не в дельте.
- Риски, откат, release-артефакты, паритет чисел П1–П4 (кроме самого способа получения базовой линии, который и есть H1) — не в дельте.
Что проверено и корректно (сверх таблицы выше)
docs/CONFIG-COMPATIBILITY.md— новый абзац про обязательность миграции базовой линии и явное указание обеих реализаций (commitWallSegmentModel/commit_wall_segment_model) фактически точны: обе функции существуют и вызываются ровно там, где написано.docs/specs/329-junction-limits.md— ревизия 7 честно фиксирует все пять закрытий и не вводит новых продуктовых решений задним числом.- Обработка
except Exceptionв_migrated_spaces()(бэкенд): падение миграции не становится вердиктом этого валидатора — по коду до этой точки уже должен был отработать барьер модели стен, так что это защитный, а не рабочий путь; не создаёт регресса H1, так как обе стороны получают одинаковую (либо мигрированную, либо сырую при одинаковом сбое) трактовку независимо друг от друга только в теории — на практике оба документа уже прошли барьер записи раньше в конвейере. - Трейлеры всех 4 коммитов —
Issue: #329,User-Visible: no; корректно: правки чинят баг ещё не выпущенной (Unreleased) фичи этого же issue — запись вdocs/CHANGELOG.md/.ru.mdуже существует сf514fb27и описывает финальное, уже исправленное поведение, отдельной строки для самого фикса не требуется. - «Одно число — один источник»: дельта не вводит и не меняет ни одной новой пользователем видимой величины (правки бэкенда, тестов и прозы документации).
Чего не проверял и почему
- Полный
pytest tests_backend(не толькоtest_junction_limits.py) — падает уже на сборе (ModuleNotFoundError: homeassistant,voluptuous), эти пакеты в окружении не установлены; тот же пробел был в r1, дельта его не касается и не обязана устранять. - Полная матрица
demo/smoke_*.mjsи полный прогон реестра мутантов — не запускал:smoke-select.mjsне нашёл ни одной связи для этой дельты (единственный изменённый символ вsrc/**— переставленный комментарий), а полный прогон реестра — предрелизный гейт (PROCESS.md §8), не гейт ревью такого размера. npm run invariants -- --config ...— дельта не меняет геометрический контракт (только удаляет мёртвый код и правит бэкенд/тесты/прозу); поведенческая часть уже закрыта зелёнымgolden:verifyпо всей матрице.
Вердикт
Красный. Единственная находка — High, блокирует. Она не переоткрывает ни одну
из пяти закрытых находок r1 (все пять закрыты корректно и по существу) и
исправляется одной строкой в scripts/mutation-gate.mjs, но npm test сейчас
красный на SHA, уходящем в dev, а это один из обязательных дешёвых гейтов этого
же процесса.