Issue: #310 User-Visible: no
13 KiB
CODE-REVIEW-310-r2
Issue: https://github.com/Matysh/houseplan-card/issues/310
Ветка: issue/310-pair-apex @ 89ac6024bca2e836ac53ed0b85fbce728c25adc9
ТЗ: docs/specs/310-pair-apex.md (ревизия 2, SPEC-REVIEW-310-r2 — зелёный)
Заход: r2 (второй код-ревью; r1 — жёлтый, High:1/Medium:1)
1. Раунд r1 и дельта
r1 проведён на SHA 0e6fbfb0 (документ docs/reviews/CODE-REVIEW-310-r1.md,
закоммичен 2e3874f1). Вердикт r1: жёлтый, High:1, Medium:1.
Дельта r1→r2 — ровно один коммит, 89ac6024 (git diff 0e6fbfb0..89ac6024):
test/wall-thickness.test.mjs | 55 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
Продуктовый код (src/**) не тронут — только новый юнит-тест и текст коммита
(правка происхождения эталона живёт в trailer'ах нового коммита, а не в файлах).
Это чисто аддитивная дельта: разбор по существу — что доказывают эти 55 строк
и честен ли новый trailer Baseline-Reviewed; остальной код унаследован из r1
без повторного вычитывания (раздел 4).
2. Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
High — Baseline-Reviewed в 0e6fbfb0 указывал на прогон ветки issue/309-junction-visual-limit (SHA 6c1534f1, предок первого коммита #310), у которого к тому же нет артефакта golden-images (job golden там прошёл зелёным без выгрузки) |
Новым коммитом 89ac6024 (историю 0e6fbfb0/de0867b0 не переписывали) добавлен честный trailer Baseline-Reviewed: .../runs/32894391916 — реальный Validate-прогон на самом SHA 0e6fbfb0, ветка issue/310-pair-apex; job golden в нём зелёный. Commit message прямо называет прежний trailer ошибкой и описывает новый как «подтверждающий прогон этого кода и эталона», не выдавая его за пре-accept review-артефакт |
Проверено gh run view 32894391916 --json headBranch,headSha,status,conclusion → headBranch: issue/310-pair-apex, headSha: 0e6fbfb0..., conclusion: success; gh run view ... --json jobs → golden: success. Commit 89ac6024 message (см. §3.1) |
| Medium (в скоупе) — обещанный ТЗ §8/риск №3 юнит «пара с коротким толстым саппортом (#271+#310)» отсутствовал в диффе | Добавлен issue 310 a short deep support pair bounds the wedge by its own length and stays hole-free (test/wall-thickness.test.mjs:3348): деградированная толстая (h=20) стена длиной 30 < 2·20 против тонкой (h=10) опоры; проверяет, что все вершины клина не выходят за min(2·halfDepth, length), и что сеточный контракт (кладка = полосы ∪ патч − клинья) чист на этой конфигурации. Комментарий в тесте фиксирует структурную невозможность интерференции с латеральным тримом #271: его карта узлов (buildMultiWallNodeMap) требует ≥3 канонических лучей, узел-пара в неё не попадает |
test/wall-thickness.test.mjs:3348-3402; мутационно перепроверено (см. §3.2) |
3. Проверка дельты
3.1 Честность нового Baseline-Reviewed
Проверено самостоятельно, не со слов автора:
gh run view 32894391916 --repo Matysh/houseplan-card \
--json headBranch,headSha,status,conclusion,workflowName
→ {"conclusion":"success","headBranch":"issue/310-pair-apex",
"headSha":"0e6fbfb00b81166bf3d9323fd21dfb86524cd3ed",
"status":"completed","workflowName":"Validate"}
gh run view 32894391916 --repo Matysh/houseplan-card --json jobs \
-q '.jobs[] | {name,conclusion}'
→ ... "golden": "success" ...
SHA прогона совпадает байт-в-байт с SHA коммита 0e6fbfb0, который несёт и код
pairButtEndTrimWedges/безлимитный mitre, и принятый PNG — то есть ссылка
больше не указывает на посторонний прогон/ветку, находка High r1 (недостоверная
ссылка = «invent a review link») закрыта фактически, не только формулировкой.
Отдельно: это не тот же самый процесс, что описан в demo/golden/README.md
(«use the golden-images artifact produced by the Linux CI job as the review
set», т.е. кандидат из CI-артефакта разбирается до golden:accept) — этот
прогон стартовал уже с закоммиченным эталоном и лишь подтверждает постфактум,
что код+эталон согласованы на Linux CI (то самое пиксельное несоответствие
шрифтов между окружениями, ради которого правило существует, тут исключено
объективно, а не на словах). Это ровно вариант «б», который r1 сама
предлагала как приемлемое закрытие («честная запись происхождения» +
согласование с владельцем) — и коммит-сообщение с этим и оформлен, автор —
владелец. Отдельно High/Medium не поднимаю: r1 явно допускала этот путь как
достаточный.
3.2 Новый юнит-тест — умеет падать
Временно откатил ограничение reach до 2·halfDepth без учёта длины стены
(self.length) в src/wall-thickness.ts:1202 и пересобрал test-build:
const reach = Math.min(2 * self.halfDepth, self.length); → const reach = 2 * self.halfDepth;
node --test --test-name-pattern "short deep support pair" test/wall-thickness.test.mjs
не ok 1 - issue 310 a short deep support pair bounds the wedge by its own length and stays hole-free
error: "wedge vertex 41.19...,-10.00... reaches past the wall's own end"
Тест красный на мутации, целится в правильную ветку. Правка отменена
(git checkout -- src/wall-thickness.ts), test-build пересобран обратно.
Гварда не тавтологична коду под тестом.
Структурный аргумент теста (интерференция с #271 невозможна, т.к.
buildMultiWallNodeMap требует ≥3 лучей) — тот же факт, что независимо
подтверждала r1 по src/wall-thickness.ts:1984,2032; сам код pairButtEndTrimWedges
и reach = Math.min(2 * self.halfDepth, self.length) в этой дельте не менялись
(существовали уже на момент r1, там же был проверен мутант butt-end-trim-unbounded) —
новый тест лишь добавляет покрытие обещанного сценария, не меняя контракт.
4. Унаследовано из r1
Без повторной проверки в r2 (документ docs/reviews/CODE-REVIEW-310-r1.md,
коммит 2e3874f1, база — SHA 0e6fbfb0), так как дельта r1→r2 их не задевает:
- Скоуп диффа (§1 r1):
linearWallJoinPatchesбез лимита,pairButtEndTrimWedges, порядок вычитания вphysicalBodyPartsдоcutPartitionBody,drawWallPreviewD. - AC1–AC4, AC6, AC7 (§3 r1) — остриё без фаски, зубец срезан, узлы ≥3 лучей
байтово прежние (
golden:verify129/129 включаяjunction-309-step/hump), 90°-пара без клина, все три мутантаpair-chamfer-returns/butt-end-trim-disabled/butt-end-trim-unboundedкрасные. - AC5 (парный сеточный контракт) — тест
issue 310 pair grid contract...(test/wall-thickness.test.mjs:3274), не менялся в этой дельте. - §5 r1 «Что проверено и корректно»: T-узлы корректно исключены из парной
ветки, читаемость реализации, трейлеры
de0867b0/0e6fbfb0(кроме содержанияBaseline-Reviewed, закрыто выше), Low-находка о дублированииsubtractWedgeFromBody/clipBodyByWedge— не поднята до Medium в r1, автор её не трогал в этой дельте, остаётся на его суждение при следующей правке области (Low, не блокирует). - §6 r1 «Чего не проверял»: полная браузерная смок-матрица,
pytest tests_backend,model-invariants, побайтовыйgolden:accept— основания не изменились (дельта r2 их тем более не касается, диф даже не трогаетsrc/**).
5. Гейты этого раунда
| Гейт | Команда | Результат |
|---|---|---|
| Typecheck | npx tsc --noEmit |
чисто |
| Unit | npm test |
1315 объявлено / 1314 pass / 0 fail / 1 skip — совпадает с хендоффом |
| Мутация нового теста | ручной откат reach в src/wall-thickness.ts + tsc -p tsconfig.test.json && fix-test-build.mjs && node --test на целевом тесте |
красный на мутации (см. §3.2), правка отменена |
| Build + бандлы | npm run build && cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js |
собран, cmp — побайтовое совпадение, git status чист |
| Docs fingerprint | — | не запускался: дельта r1→r2 не трогает src/** (только тест) |
| Смок-выборка | node scripts/smoke-select.mjs --base 0e6fbfb0 --head HEAD |
«Исполняемого frontend-диффа нет (src/**/*.ts не тронут)» — выбирать нечего, браузерные смоки к этой дельте неприменимы |
| Golden | npm run bundle:sync && npm run golden:verify |
129/129 passed (перепрогнан лично на текущем HEAD, не только со слов автора) |
| Backend | — | не запускался: Python не тронут |
| Model invariants | — | не запускался: диф не меняет модель/геометрию/конфиг, только тестовый файл |
| «Одно число — один источник» | — | неприменимо: дельта не добавляет и не меняет пользовательски видимых величин |
6. Чего не проверял
- Полную браузерную смок-матрицу (190 файлов) и
performance_smoke— избыточно, дельта не трогаетsrc/**вовсе;smoke-selectподтвердил «выбирать нечего». python -m pytest tests_backend— Python не тронут ни в этом раунде, ни накопленным диффом сorigin/dev.scripts/model-invariants.mjs— геометрия/модель/конфиг не менялись.- Скриншоты документации —
check-docs.mjsне запускался (диф не трогаетsrc/**), содержательных изменений публичных экранов эта дельта не вносит. - Повторную полную вычитку кода
pairButtEndTrimWedges/physicalBodyParts/drawWallPreviewD— не требуется, эти файлы не в дельте r1→r2, r1 их проверила построчно (§4 «Унаследовано из r1»).
7. Итог
Обе находки r1 закрыты по существу и подтверждены независимо (не со слов
автора): High — реальный, честно описанный CI-прогон на правильном SHA с
зелёным golden; Medium — юнит написан по плану ТЗ, мутационно проверен на
способность падать. Новых находок в дельте r1→r2 нет. Продуктовый код не
менялся с r1, весь его разбор наследуется без повторной проверки.
Вердикт: зелёный.