docs: review document for #309
Validate / docs (push) Successful in 27s
Validate / process-workflow-sync (push) Successful in 47s
Validate / process-gate (push) Failing after 59s
Validate / provenance (push) Successful in 1m5s
Validate / changes (push) Successful in 43s
Validate / reuse (push) Successful in 1m2s
Validate / hacs (push) Failing after 16s
Validate / hassfest (push) Failing after 16s
Validate / backend (push) Failing after 4m31s
Validate / frontend (push) Successful in 4m46s
Validate / golden (push) Failing after 17m34s
Validate / smoke (3) (push) Failing after 17m56s
Validate / smoke (2) (push) Failing after 18m18s
Validate / smoke (1) (push) Failing after 18m53s
Validate / smoke_done (push) Skipped
Validate / performance_smoke (push) Failing after 18m46s

Issue: #309
User-Visible: no
This commit is contained in:
claude[bot]
2026-08-25 19:23:54 +00:00
parent a3a75ccbf3
commit f2460dc81d
+130
View File
@@ -0,0 +1,130 @@
# CODE-REVIEW-309-r2
Issue: https://github.com/Matysh/houseplan-card/issues/309
Ветка: `issue/309-junction-visual-limit`
Ревьюер: Claude (код-ревью), сессия отдельна от автора (Codex)
Диапазон полного диффа: `origin/dev...HEAD`, HEAD = `a3a75ccbf3e1008a4ab18966a608f7f61f64745f`
Заход: r2 · блокирующих циклов израсходовано 1 из 4 (потрачен на жёлтый r1;
зелёный вердикт цикл не образует, #227)
## Скоуп проверки — по дельте, не заново (PROCESS §2.9)
Предыдущий раунд (r1) — вердикт жёлтый, документ
`docs/reviews/CODE-REVIEW-309-r1.md`, зафиксирован на SHA `e85bae70`
(`origin/dev...HEAD` на тот момент = `101cf709` + `e85bae70`).
Дельта этого раунда: `git diff e85bae70..HEAD`.
```
docs/reviews/CODE-REVIEW-309-r1.md | 242 +++++++++++++++++++++++++++++++++++++
test/golden-matrix.test.mjs | 2 +-
2 files changed, 243 insertions(+), 1 deletion(-)
```
Один продуктовый/гейтовый файл тронут — `test/golden-matrix.test.mjs`, один
хунк, класс B (`test/**`), коммит `6c1534f1` (`Issue: #309`,
`User-Visible: no` — корректно, правка не меняет поведение продукта).
`docs/reviews/CODE-REVIEW-309-r1.md` — публикация документа предыдущего
раунда (класс C, не автор задачи, не предмет разбора).
**Рёбейза не было**: `git merge-base HEAD origin/dev` = `dc68868` — та же
точка, что была базой на старте r1. Контракт поведения не менялся, новая
подсистема не задета, объём дельты (одна строка) несопоставим с задачей.
Условия для полного повторного разбора (§2.9) не наступают — дельта
локальна, разбор по ней же.
`src/wall-thickness.ts` (единственный продуктовый файл во всём диффе задачи)
дельтой r2 не тронут — не перечитывался заново, см. «Унаследовано из r1».
## Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| **M1** (в скоупе): `npm test` красный на SHA `e85bae70` — `not ok 378` в `test/golden-matrix.test.mjs:370`, `assert.equal(GOLDEN_MATRIX_VERSION, 45)` при фактическом `GOLDEN_MATRIX_VERSION = 46` (`demo/golden/matrix.mjs:4`, поднят коммитом `101cf709`) | Коммит `6c1534f1` (`test: pin the golden matrix guard to version 46`) меняет `test/golden-matrix.test.mjs:370` `45 → 46` | `git diff e85bae70..HEAD -- test/golden-matrix.test.mjs` (см. дельту выше); локально `npm test` → `1309 pass / 0 fail / 1 skipped` (прогнан в этом раунде, см. таблицу гейтов); изолированно `node --test test/golden-matrix.test.mjs` → `39/39`; внешне — CI на SHA `6c1534f1` (байтово тот же код, что и текущий HEAD): run [32886626656](https://github.com/Matysh/houseplan-card/actions/runs/32886626656), **Validate: success**, job `frontend`/`docs`/`golden`/`smoke (1,2,3)`/`performance_smoke`/`process-gate`/`provenance` — все `success` (`hacs`/`hassfest`/`backend` — `skipped`, путь-фильтр, диффом не тронуты) |
| Low (снята с запиской в r1, не открыта заново): AC5 доказана не на «полном экспорте 13/24», а на минимальном вырезе + эквивалентной заменой `smoke_real_plan_masonry.mjs` | Не предмет этого раунда — дельта её не касается, решение ревьюера в r1 остаётся в силе | `docs/reviews/CODE-REVIEW-309-r1.md`, раздел «Low» |
## Унаследовано из r1 (без повторной проверки)
Продуктовый код (`src/wall-thickness.ts`) не менялся между `e85bae70` и
текущим HEAD — принимаю без повторного чтения весь разбор r1, зафиксированный
в `docs/reviews/CODE-REVIEW-309-r1.md` (SHA `e85bae70`, код идентичен на
HEAD `a3a75ccb`):
- Технический контракт: `VISUAL_MITRE_LIMIT = 1.5·max(h)`, `chamferApex`
(плоская фаска), скип парных патчей на узлах ≥3 лучей.
- AC1–AC4, AC6–AC8 — доказаны (юниты, golden `junction-309-{spike,hump,step}-dark`,
16 старых `junction-*` + `junction-owner-repro-dark` байтово прежние, 4
мутанта AC7 + адаптированный `junction-fans-disabled`, диффовое
подтверждение не-скоупа #249: `MULTI_WALL_JOIN_LIMIT`/`multiWallBevelCutsAt`/
room-contour mitre вне диффа).
- AC5 — доказана частично, Low снята с запиской (эквивалентная замена
`smoke_real_plan_masonry.mjs`, gapCount: 0 на двух реальных этажах).
- Математическое доказательство, что `chamferApex` не может выродиться в
`null` при вызовах из обоих сайтов (`linearWallJoinPatches`,
`junctionNodeGeometry`).
- Отклонение от буквы ТЗ §3.2 (фаска перпендикулярна направлению
«узел→вершина», а не буквальной биссектрисе секторов при неравных
полутолщинах) — согласовано с `WALL-THICKNESS.md:206-219`, не оспаривается.
- Гейты r1: `tsc` зелёный, `build`+3 копии бандла байтово совпадают,
`check-docs.mjs` зелёный (7 файлов/10 ссылок), `golden:verify` 129/129,
смоки `junction_holes`/`real_plan_masonry` (прямые совпадения) и
`multiwall_junction`/`junction_patch_resilience` (зарегистрированные связи)
лично прогнаны и зелёные, `render_perf` OK, model-invariants — не
применимо (геометрия узлов, не запись толщины/`layout`/`marker.space`/
`open_spans`), backend pytest — не применимо (`custom_components/**/*.py`
вне диффа).
## Гейты, прогнанные в этом раунде
| Гейт | Команда | Результат |
|---|---|---|
| typecheck | `npx tsc --noEmit` | зелёный |
| unit (полный) | `npm test` | `1309 pass / 0 fail / 1 skipped` из 1310 — зелёный (M1 закрыт) |
| unit (точечно, файл находки) | `node --test test/golden-matrix.test.mjs` | `39/39` зелёный |
| build + 2 копии бандла (dist ↔ custom_components) | `npm run build && cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` | зелёный, байты совпадают; `git status --short` после сборки — чисто (собранный бандл byte-identical закоммиченному) |
| docs fingerprint | `node scripts/check-docs.mjs` | зелёный (7 файлов, 10 внешних ссылок) — дельта r2 не трогает `src/**`, прогнан для очистки сомнений, не по обязанности |
| выбор смоков по дельте r2 | `node scripts/smoke-select.mjs --base e85bae70 --head HEAD` | «Исполняемого frontend-диффа нет (src/**/*.ts не тронут)» — смоки этой дельтой не выбираются, выбирать нечего |
| внешнее подтверждение (CI) | `gh run view 32886626656 --json jobs` на SHA `6c1534f1` (код идентичен HEAD `a3a75ccb`) | `Validate: success`; `frontend`/`docs`/`golden`/`smoke (1,2,3)`/`performance_smoke`/`process-gate`/`provenance` — все `success` |
## Чего не проверял в этом раунде (и почему)
- `python -m pytest tests_backend` — `custom_components/**/*.py` вне диффа
задачи целиком и вне дельты r2; наследую r1 «не применимо».
- `node scripts/model-invariants.mjs` — дельта r2 не создаёт и не переносит
записи толщины/`layout`/`marker.space`/`open_spans`; критерий §8 не
наступает, унаследовано из r1.
- Браузерные смоки (`junction_holes`, `real_plan_masonry`,
`multiwall_junction`, `junction_patch_resilience`, остальные из хендоффа
автора) — не перезапускал: `smoke-select.mjs` на дельте r2 не выбрал ни
одного (тест-файл не относится к `src/**`), а геометрия `wall-thickness.ts`
не менялась со времени их прогона в r1. Перезапуск дал бы тот же
результат ценой пяти минут без нового сигнала.
- `npm run golden:verify` — не перезапускал; дельта r2 не меняет ни
геометрию, ни матрицу сцен (сама версия матрицы фактическая осталась 46,
поменялось только ожидание в тесте), 129/129 из r1 остаётся в силе.
- Мутационный гейт (`scripts/mutation-gate.mjs`) — не перезапускал, продуктовый
код не менялся с r1, где все 5 мутантов (AC7 a-d + адаптированный
`junction-fans-disabled`) поймали регрессию.
## Один источник числа
Дельта r2 не вводит и не меняет ни одной пользовательски видимой величины —
правка касается исключительно внутреннего счётчика версии тестовой матрицы
(`GOLDEN_MATRIX_VERSION`), не рендерящегося и не сохраняемого в конфиге.
Проверка `test/single-source-numbers.test.mjs` входит в зелёный `npm test`
этого раунда без отдельного разбора: числа геометрии стыков (порог `1.5h`,
формы фасок) не менялись с r1, где источник уже проверен как единственный
(`chamferApex`/`linearWallJoinPatches`/`junctionNodeGeometry`).
## Вывод
Единственная блокирующая находка r1 (M1: красный обязательный гейт
`npm test` на SHA `e85bae70` из-за забытого хардкода версии golden-матрицы)
закрыта точечным коммитом `6c1534f1`, подтверждена локальным прогоном полного
`npm test` (1309/0), изолированным прогоном упавшего файла (39/39) и внешне —
зелёным CI-прогоном на байтово идентичном коде. Продуктовый код
(`src/wall-thickness.ts`) не менялся с r1 и весь его разбор (AC1–AC8, находки,
математическое доказательство корректности `chamferApex`, гейты) наследуется
без повторной проверки. Новых находок дельта r2 не создаёт.
**Вердикт: зелёный.** Задача готова к мержу в `dev`.