Files
houseplan-card/docs/reviews/CODE-REVIEW-234-r2.md
T
claude[bot] 0df5db8b1c
Validate / docs (push) Failing after 20s
Validate / reuse (push) Successful in 55s
Validate / changes (push) Successful in 1m13s
Validate / provenance (push) Failing after 1m21s
Validate / process-gate (push) Failing after 1m22s
Validate / hacs (push) Failing after 15s
Validate / hassfest (push) Failing after 11s
Validate / frontend (push) Successful in 3m23s
Validate / backend (push) Failing after 4m25s
Validate / golden (push) Failing after 9m54s
Validate / performance_smoke (push) Failing after 11m9s
Validate / smoke (push) Failing after 20m20s
docs: review document for #234
Issue: #234
User-Visible: no
2026-08-21 16:56:20 +00:00

16 KiB
Raw Blame History

CODE-REVIEW-234-r2

  • Issue: #234 — толщина отрезка цепочки стен расходится между превью и записью
  • Этап: код-ревью (PROCESS.md §2.7)
  • Заход: r2 · блокирующих циклов израсходовано 0 из 4 (r1 был зелёным, бюджет не тратил — правило #227)
  • Материал: git log --oneline origin/dev..HEAD, git diff origin/dev...HEAD
  • Коммит на вершине: c8e9597 (единственный коммит диапазона, dev@1e2eba9)
  • Трейлеры коммита: Issue: #234 · User-Visible: yes — оба changelog правятся в этом же коммите

Почему разбор полный, а не по дельте

Раунд r1 (SHA a0878cd) закончился зелёным вердиктом без замечаний, требующих правки. Между r1 и этим заходом код задачи не менялся — но ветка была ребейзнута на ушедший вперёд dev (в dev за это время влилась #229, слияние коллинеарных перегородок, тоже правящая src/houseplan-card.ts). PROCESS.md §2.10 и §7.2 прямо называют этот случай исключением из сокращения по дельте: «после ребейза на ушедший вперёд dev это другой код». Поэтому этот документ — полный разбор AC1…AC9, а не разбор одной строки диффа.

Практически это означает: код резолвера и всех шести точек вызова я прочитал и проверил заново на текущем HEAD, а не принял на веру утверждение «правок кода не потребовалось» из комментария владельца.

Что проверил

1. Диагноз и контракт по коду

Построчно сверил src/wall-face-graph.ts и шесть точек в src/houseplan-card.ts с контрактом §6 ТЗ (docs/specs/234-chain-segment-thickness.md):

  • chainSegmentCms(segmentCount, recorded, activeCm, defaultCm) — правило «своя запись > 0 → предыдущий валидный → activeCm → defaultCm» реализовано буквально (src/wall-face-graph.ts:78-97); defaultCm невалидный вызывающего приводится к минимальной толщине 1 см, а не к выдуманному значению — это расширяет контракт консервативно, не меняя его.
  • wallChainSegments лишена собственного fallback: принимает cms[i] как есть (wall-face-graph.ts:112-121).
  • Все шесть мест переведены на резолвер:
    1. превью цепочки (houseplan-card.ts:18610-18621);
    2. незамкнутая цепочка → перегородки, _finishWallChain (:6576-6584);
    3. замкнутая цепочка → перегородки (:13089-13099);
    4. замкнутый контур → комната, edgeCms/source (:12710-12742);
    5. подсветка инструмента «Толщина», _wallSourceCmAt (:12426-12435);
    6. батч граней стен, _applyWallFaceBatch (:12491-12497) — шестое место, найденное автором сверх каталога ТЗ §3; подтверждаю, что оно действительно использовало wallChainSegments(..., DRAW_WALL_DEFAULT_CM) до правки и теперь читает тот же резолвер.
  • Инвариант длины: точка и её толщина пишутся одной операцией в _markupClick (:7301-7312), _persistActiveDraftSegment больше не решает, писать ли толщину (:7426-7442, комментарий явно называет причину). Четыре пути чтения черновика (:2618, :7243, :7383, :12817) приведены к длине через новый _adoptDraftCms (:7432-7442), который использует тот же резолвер и логирует дозаполнение в console.debug.

2. Гейты — прогнаны на HEAD (c8e9597) в этой сессии

Команда Результат
npx tsc --noEmit чисто
npm test 1019 pass / 0 fail
npm run build собран
cmp dist/... custom_components/.../houseplan-card.js совпадают побайтово
cmp dist/... demo/srv/assets/houseplan-card.js совпадают побайтово
node scripts/mutation-gate.mjs --check зелёный, все id, включая три новых, структурно валидны
--id=chain-thickness-falls-back-to-default поймано 1 из 1
--id=chain-thickness-preview-diverges поймано 1 из 1
--id=chain-thickness-length-invariant-dropped поймано 1 из 1
node demo/smoke_wall_chain_thickness.mjs OK
node demo/smoke_draw_wall_thickness.mjs OK (регресс §12.4 ТЗ)
node demo/smoke_wall_thickness_transition.mjs OK (регресс §12.4 ТЗ, sharedKept: true — общие участки между комнатами не задеты)

В отличие от автора, у ревьюера Chromium в песочнице доступен — целевой смок и оба регрессионных прогнаны напрямую, не со слов.

Тест умеет падать — проверено, а не предположено. Временно вернул резолверу старую подмену (own ?? previous ?? fallbackTail → own ?? fallbackTail), пересобрал бандл, скопировал в demo/srv/assets и прогнал целевой смок: он покраснел ровно на том свойстве, которое проверяет (gapInheritedPrevious: expected true, got false). После проверки дерево возвращено в исходное состояние (git checkout -- ...), пересборка и сверка трёх копий подтвердили чистоту рабочей копии.

3. AC — чем доказано

AC Требование Статус
AC1 chainSegmentCms возвращает segmentCount строго положительных чисел на любом входе, включая явный 0 зелёный, unit, таблица мусорных входов покрывает границу > 0
AC2 Пропуск наследует предыдущий валидный → activeCm → defaultCm зелёный, unit
AC3 Превью и запись дают идентичный вектор на одних данных зелёный, unit (the preview and the writers cannot disagree)
AC4 Цепочка 30/30/(пропуск) сохраняется как 30,30,30 зелёный, unit + смок
AC5 Контур → комната сохраняет распределение превью, общие участки не задеты смок sharedKept: true + прочитан код: edgeCms[source] берётся из того же резолвера, раскладка по интервалам (applyWallThicknessToNewRoom/setWallThickness) не тронута — вне скоупа задачи и не изменена
AC6 Подсветка инструмента = записанное значение зелёный, смок (highlightMatchesStored)
AC7 Черновик с segments короче points-1 дочиняется по правилу §6 зелёный, unit + смок (legacyResumedFullVector, legacyGapsInheritedThirty)
AC8 Точка не добавляется без валидной толщины; длины согласованы проверено чтением, не исполнением: атомарная запись в _markupClick структурно исключает расхождение; отдельного сквозного теста последовательности кликов нет — граница признана автором и приемлема, т.к. поведение выражено на уровне одного метода, а не координации двух
AC9 Обе записи changelog в том же коммите в диффе c8e9597, подтверждено git show --stat

4. Взаимодействие с #229 (ребейз)

Автоматическое слияние src/houseplan-card.ts и scripts/mutation-gate.mjs — затронутые участки не пересекаются (резолвер chainSegmentCms и spaceMergeGeometry из #229 работают на разных строках). Косвенное подтверждение: npm test даёт 1019 тестов (было 989 на a0878cd, dev принёс +30 своих), все зелёные; регресс smoke_wall_thickness_transition.mjs (тоже смежный с геометрией стен) зелёный. Отдельно проверил, что смок smoke_wall_chain_thickness.mjs строит неколлинеарную цепочку (прямой угол между вторым и третьим отрезком), то есть автослияние коллинеарных перегородок (#229) в этом сценарии не участвует — это не создаёт риска для AC4, но и не является для них доказательством совместной работы; совместная работа проверена лишь тем, что полный набор юнит-тестов (включающий тесты #229) зелёный.

Закрытие раунда r1

r1 (SHA a0878cd) — вердикт зелёный, единственная находка Low, снятая с записью, без обязательной правки.

Находка r1 Чем закрыта Где видно
Low: недостижимая ветка if (cm == null) { … return; } в _markupClick, дублирующая проверку _canAppendRoomDraftPoint Не требовала правки в r1 (снята с записью: поведения не меняет, диапазон 1…100 закрыт UI). Ребейз этот код не тронул — строки идентичны src/houseplan-card.ts:7301-7308 (проверка _canAppendRoomDraftPoint на :7170-7171) — прочитано на HEAD, содержимое совпадает с описанием в комментарии r1

Новых находок в этом заходе нет.

Унаследовано из r1

Формально это полный разбор (см. раздел выше про причину), поэтому ничего не принято «на слово» — каждый пункт ниже перепроверен на текущем HEAD, а не скопирован из документа r1:

  • Диагноз шести точек и их перевод на chainSegmentCms — перечитан построчно на c8e9597, совпадает с описанием r1 (docs/reviews/CODE-REVIEW-234-r1.md, SHA a0878cd) и с ТЗ §3/§6.
  • AC1–AC7, AC9 — тесты и смоки, зелёные ранее и сейчас, прогнаны заново.
  • AC8 — тот же способ доказательства («прочитано, не исполнено»), что и в r1; структура кода не изменилась.
  • Продуктовая рамка и touch-классификация (not exposed) — унаследованы из ревью ТЗ (docs/reviews/SPEC-REVIEW-234-r2.md), рабочий код их не касается.

Единственное, что действительно не перепроверялось повторно, — сам текст ТЗ: ребейз его не менял, а спек-ревью r1/r2 уже дало зелёный вердикт по контракту.

Находки

Нет High. Нет Medium. Low из r1 — не новая, подтверждена присутствующей и безвредной (см. таблицу закрытия выше), новой правки не требует.

Что проверено и корректно

  • Единственный резолвер толщины, все шесть точек записи/чтения/подсветки на нём, wallChainSegments без собственного fallback — контракт §6 ТЗ реализован буквально.
  • Инвариант длины _draftSegmentCms/_path закреплён атомарной записью и дозаполнением при чтении черновика.
  • Гейты: typecheck, полный набор юнит-тестов, сборка со сверкой трёх копий бандла, целевой и оба регрессионных смока, точечные прогоны трёх новых мутаций — все зелёные, и целевой смок проверен на способность падать лично.
  • Трейлеры и changelog (RU+EN) в том же коммите.
  • i18n не затронут (строки не менялись, src/i18n/*.json в диффе нет) — соответствует §9 ТЗ.
  • Ребейз не создал конфликта с #229 на уровне поведения: полный прогон тестов зелёный, затронутые участки не пересекаются.

Чего не проверял и почему

  • npm run golden:verify — не прогонял. Diff не меняет ни одной формулы рендера (геометрия, цвет, слои), только источник значения толщины, подаваемого в уже существующие формулы; то же обоснование, что в r1.
  • python -m pytest tests_backend — не прогонял, custom_components/**/*.py не тронут.
  • Полный прогон 127 смоков — не прогонял, только целевой плюс два названных в плане автотестов ТЗ (§12.4) регресса на смежных поверхностях (толщина при рисовании, переход толщины при сплите). Остальные смоки не касаются тронутых поверхностей (рисование цепочки, толщина, черновики).
  • Полный/дорогой прогон mutation-gate (весь набор мутаций, а не структурный --check плюс три точечных) — не прогонял, это предрелизный гейт (PROCESS.md §8), не гейт ревью.
  • Perf-профили — не прогонял: в AC они не названы, резолвер чистый и вызывается на кадр рисования на массиве ограниченной длины (MAX_DRAFT_POINTS), риск не следует из диффа.

Вывод

Ребейз не изменил код задачи и не создал конфликта с #229. Все шесть точек резолвера, инвариант длины и AC1…AC9 проверены на текущем HEAD — не унаследованы слепо. Единственная находка r1 (Low) осталась на месте, безвредна, новой правки не требует. Новых находок нет.

Вердикт: зелёный.