27 KiB
CODE-REVIEW-648-r3
- Issue: https://github.com/Matysh/houseplan-card/issues/648 — «Sections: вертикальный resize карточки и container-owned высота плана»
- Этап:
S7-code-review(код-ревью, PROCESS.md §2.7) - Трек: полный (
P2, см.docs/reviews/SPEC-REVIEW-648-r1.md) - Материал:
git log --oneline origin/dev..HEADиgit diff origin/dev...HEAD; вершина ветки — ровно4168b1cfcedcc7979e17a380427d4466b45f6b2b, база —origin/dev=7372e977b89a0a907b67cf00c1b2cd2b9ed2ddfa. Семь коммитов (в порядке предок→потомок):230813b3(docs, SPEC-REVIEW-648-r1),2452c51d(feat, User-Visible: yes),dc0a5ccc(test),b9fe9166(test),180239f4(docs, CODE-REVIEW-648-r1),4168b1cf(docs, индекс),8f13325c(test). - Заход: r3 · блокирующих циклов израсходовано 2 из 4 (по заданию оркестратора)
- Роль: ревьюер кода (не автор)
Аномалия материала — зафиксировано, не блокирует
Прежде чем перейти к разбору: я не смог найти в репозитории и в issue #648 никакого артефакта раунда r2 код-ревью.
docs/reviews/содержит толькоCODE-REVIEW-648-r1.md(git-история подтверждает —git log --all -- 'docs/reviews/CODE-REVIEW-648*'называет единственный коммит180239f4).- В issue 11 комментариев; последний — от автора в 13:07:27 UTC («Medium-1 ревью r1 исправлен... Жду зелёного push Validate и затем заново запускаю S7»). Комментария с вердиктом r2 нет.
- Таймлайн меток issue показывает
S7-code-review→S6-in-progressв 13:04:13 (это возврат по жёлтому r1), затемS6-in-progress→S7-code-reviewв 13:11:02 (после коммита с RU/DE-фиксом), и ещё одну перестановку той же метки в 13:15:30→13:15:32 без промежуточного комментария. Ни один из этих переходов не сопровождён вторым жёлтым/красным комментарием ревьюера. - SHA, которые называл r1 (
b7fb7cf7…,76a1d5c0…и т. д.), в текущей истории не существуют (git cat-file -t— «could not get object info»): ветка была перебазирована на ушедший вперёдdev(текущийorigin/dev—7372e977, слияние другой задачи #649; старая база r1f68878cc— предок текущегоdev, не наоборот). Это подтверждённый ребейз на продвинувшийсяdev.
Согласно условию задания «дельта не локальна, если… ребейз на ушедший вперёд
dev» — это ровно тот случай, поэтому ниже полный разбор, а не
дифф-от-r2. Как единственный найденный предыдущий раунд использую
CODE-REVIEW-648-r1.md; расхождение номера (r1 найден, а не r2) — вопрос
целостности конвейера/бюджета, а не кода этой задачи, и не влияет на
техническую строгость проверки ниже: полный прогон гейтов и AC перекрывает
любые находки, которые мог бы содержать пропавший r2.
Скоуп ревью
Диапазон правит:
src/houseplan-card.ts— публичное реактивное свойствоlayout(HA-сигнал), геттер_containerOwnedHeight(panelHost || layout === 'grid'),getGridOptions()→{ columns: 'full', rows: 10, min_rows: 6 }, использование_containerOwnedHeightвместоpanelHostв расчёте высоты mode-transition,measuredCardHeaderHeight,settleSoftStageLayoutи inlinestyle="height:…";src/boot-soft-layout.ts— переименование параметраpanelHost→containerOwnedHeightв тех же двух функциях (поведение не меняется, кроме нового источникаtrue);src/styles/base.styles.ts—:host([layout="grid"])добавлен рядом с:host([panel-host])во всех соответствующих селекторах;demo/smoke_sections_resize.mjs(новый, 225 строк) — browser smoke;demo/smoke_houseplan_panel.mjs— соседний regression-oracle (AC7);test/houseplan-panel.test.mjs— обновлён текстовый oracle контракта #486;scripts/mutation-registry.mjs(4 новых мутанта),scripts/smoke-links.mjs(регистрация smoke по символам);- документация:
docs/ARCHITECTURE.md,docs/CHANGELOG.md,docs/CHANGELOG.ru.md,docs/STATUS-FEATURES.md,docs/TESTING.md,docs/USER-GUIDE.md,docs/USER-GUIDE.ru.md; dist/**,custom_components/houseplan/frontend/**,demo/srv/assets/**(класс D, пересобранный бандл — сверено побайтно, «Как проверялось» п.3);docs/reviews/CODE-REVIEW-648-r1.md,docs/reviews/SPEC-REVIEW-648-r1.md,docs/reviews/INDEX.md— материал предыдущих раундов, не код продукта.
Первый вопрос — какую строку docs/SCOPE.md задача обслуживает: прямого
Core-job нет, это инфраструктурная UX-правка поверхности, на которой живут
J1–J7 для персоны Home admin (Sections — основной тип dashboard в HA; задача
не расширяет функциональность плана и не входит в «Out of scope»). Так же
было принято ревьюером ТЗ в SPEC-REVIEW-648-r1.md — конфликта со SCOPE.md
нет; за прошедшие раунды это не изменилось.
Как проверялось
- Прочитаны
docs/SCOPE.md,AGENTS.md,docs/process/REVIEWER.md(в материале отсутствует — читал разделы PROCESS.md §2.4, §2.7, §2.10, §4, §7.2, §8, §12 напрямую), тело issue #648 (gh issue view 648 --json body, разделы ТЗ, K1–K12, AC1–AC10) и все 11 комментариев issue (gh issue view 648 --json comments), таймлайн меток (gh api repos/Matysh/houseplan-card/issues/648/timeline). - Прочитан полный
git diff origin/dev...HEADпо каждому изменённому файлуsrc/**, обоим browser smoke, юнит-тесту,mutation-registry.mjs,smoke-links.mjs, всем изменённымdocs/*.md. - Пересобрал бандл (
npm run build, затемnpm run bundle:sync) на материале —git status --porcelainдо и после пуст:dist/**,custom_components/houseplan/frontend/**иdemo/srv/assets/**побайтно совпадают с пересобранными. - Дешёвые гейты (
typecheckвнутриbuild,npm test,npm run build) уже подтверждены зелёным Validate на этом точном SHA (ссылка в задании); тем не менее перегналnpm run build(п.3) и полныйnpm testсам для собственной уверенности перед прогоном смоков на пересобранном бандле — 3086 тестов, 3085 pass, 1 skip, 0 fail — совпадает с отчётом Validate и с r1. - Прогнал сам, так как диф трогает
src/**:node scripts/no-new-any.mjs(0 новыхanyв 31 добавленной строке),node scripts/no-new-private-writes.mjs(0 новых записей в приватное состояние из 227 добавленных строк smoke),npm run bundle:budget(initial View 291761 B — совпадает с r1, в пределах потолка),node scripts/check-docs.mjs --screenshots=warn(WARN про фингерпринт скриншотов — ожидаемо, диф не меняет пиксели плана),node scripts/validate-commit-provenance.mjs --range=origin/dev..HEAD(exit 0) — все зелёные. - Прогнал
node scripts/smoke-select.mjs --base origin/dev --head HEAD— «прямое совпадение» вернуло те же 19 смоков, что и в r1 (диф с r1 не меняет затронутые символыsrc/**), включая оба названных в AC (smoke_sections_resize.mjs,smoke_houseplan_panel.mjs). Прогнал их плюсsmoke_render_invalidation.mjsиsmoke_warm_dialogs.mjs(те же точечные регрессионные свидетели рефактораpanelHost → containerOwnedHeight, что r1 выбрал по символам_bootSoft/_hdrH/_warmSlot) — все четыре зелёные (полный вывод в п.7 ниже и в разделе «Что проверено»). - Через
node scripts/mutation-gate.mjs --id=<id>прогнал все четыре защитных мутанта задачи по отдельности:sections-grid-falls-back-to-viewport-height,sections-editor-transition-uses-window-height,sections-grid-drops-stage-flex-chain,sections-resize-drops-stage-refit-observer— каждый вывел «поймано 1 из 1» (smoke_sections_resize.mjsкраснеет предметно на каждом),git status --porcelainпуст после каждого прогона (скрипт сам возвращает патч и пересобирает). - Целевая проверка закрытия Medium-1 из r1: прочитал полный diff коммита
8f13325c—minimumGeometryтеперьfor (const language of ['ru', 'de']), каждая итерация делает реальныйcard.setConfigс этимlanguageи меряет геометрию; финальный булевminimumSixRowsStayContainedInRuAndDeтребует.every(...)по обеим записям, а не только по одной. Прогнал самsmoke_sections_resize.mjsцеликом —minimumSixRowsStayContainedInRuAndDe: trueв выводе. - Проверил трейлеры всех коммитов (
git show -s --format='%H%n%s%n%b') —Issue: #648на каждом из шести некод-ревью-документных коммитов;User-Visible: yesтолько на2452c51d(feat), иdocs/CHANGELOG.md+docs/CHANGELOG.ru.mdвходят именно в этот коммит (провереноgit show --stat 2452c51d). - Проверил «одно число — один источник» (§8):
grep -n "rows: 10\| min_rows\|632\s*px\|376\s*px"поsrc/houseplan-card.tsи всем изменённымdocs/*.md— единственное определение чисел 10/6 — возвратgetGridOptions(); документация переиздаёт их прозой без отдельного хардкода. - Проверил
space-card.ts(grepgetGridOptions|layout) — не получил ни нового реактивного свойства, ни grid-defaults; юнит-тестtest/houseplan-panel.test.mjsподтверждает тем жеassert.doesNotMatch. - Прочитал комментарий
@pando80от 13:06:46 UTC: внешний автор подтвердил на реальном HA-дашборде Sections (grid_options: rows: 6, columns: full, HA/интеграция 1.78.0-beta.2, включая полный restart), что план вписывается в карточку без переполнения. Это не гейт этого ревью и не заменяет автотест, но существенно снижает остаточный риск r1 «реальное поведение HA Sections не проверено» (см. «Чего не проверял»). - Инварианты модели (
npm run invariants) иpytest tests_backendне прогонял — диф не меняет геометрическую модель (стены/комнаты/ устройства) и не касаетсяcustom_components/**/*.py; обоснование подробнее в «Чего не проверял».
Закрытие раунда r1 (единственный найденный предыдущий раунд)
| Находка r1 | Чем закрыта | Где видно |
|---|---|---|
Medium-1: AC3 обещает browser smoke RU/DE для минимальной высоты, но demo/smoke_sections_resize.mjs проверял только language: 'ru' |
Коммит 8f13325c переписал блок minimumGeometry в цикл for (const language of ['ru', 'de']) с реальным card.setConfig({..., language}) на каждой итерации; итоговый флаг minimumSixRowsStayContainedInRuAndDe требует .every(...) по обеим записям |
demo/smoke_sections_resize.mjs:91-104,199-201; лично прогнан — minimumSixRowsStayContainedInRuAndDe: true в выводе (см. «Как проверялось» п.8) |
Унаследовано из r1 (не перепроверялось заново, а подтверждено полным прогоном)
Поскольку разбор в этом раунде полный (ребейз на ушедший вперёд dev), ниже
не «унаследовано без проверки», а формально повторно подтверждено —
перечисляю соответствие r1, чтобы не дублировать формулировки находок:
- AC1, AC2, AC4–AC10, четыре защитных мутанта, оба relevant browser smoke,
трейлеры/changelog,
typecheck/npm test/npm run build/no-new-any/no-new-private-writes/bundle:budget/check-docs— то же самое доказательство, что вCODE-REVIEW-648-r1.md, лично перегнано заново в этом раунде на текущем SHA (см. «Как проверялось» пп. 4–7, 9–11) и совпадает с описанием r1 буквально (тот же код:2452c51dсодержит идентичный итог трёх исходных коммитов, которые ревьюировал r1, просто под новыми SHA после ребейза). - Остаточный риск «реальное поведение HA Sections вне репозитория не
проверено» — не закрыт кодом (репозиторий по-прежнему не может это
проверить исполнением), но частично снижен независимым внешним
подтверждением
@pando80на реальном HA (см. «Как проверялось» п.12) — остаётся в «Чего не проверял» с этим уточнением.
Находки
Ни одной новой находки. Единственная находка r1 (Medium-1) закрыта предметно и проверена исполнением (см. таблицу выше).
Что проверено и корректно
- AC1 —
getGridOptions()возвращает ровно{ columns: 'full', rows: 10, min_rows: 6 }безmax_rows(src/houseplan-card.ts:3762);executableGridDefaults: true;getCardSize() === 12не изменён (src/houseplan-card.ts:3758);space-card.tsбезgetGridOptions— подтверждено grep и юнитом. - AC2 —
defaultTenRowsFillTheSlot: true;gridUsesContainerOwnedInlineHeight: true;gridSignalReflectsForScopedCss: true— реальныйgetBoundingClientRectна живом бандле, допуск 1 px,cardScroll/haCardScroll≤ 1. - AC3 —
minimumSixRowsStayContainedInRuAndDe: true— теперь по обеим локалям (см. закрытие Medium-1 выше), header не обрезан, меню достижимо в обеих. - AC4 — серия 6→10→14→6:
resizeChangesStageAtEveryRowCount: true; мутантsections-resize-drops-stage-refit-observerкраснит smoke — прогнан лично. - AC5 —
allEditorsStayInsideTheFixedSlot: true,editorTransitionFramesUseTheFixedSlot: true,editorTransitionsLeaveNoBusyTail: true— реальныйcard._modeTransition.state, не форсированное приватное состояние; мутантsections-editor-transition-uses-window-heightкраснит smoke — прогнан лично. - AC6 —
explicitGridOptionsStayExternal: true—Object.freeze(config)- сверка
JSON.stringify(config) === configBeforeиcard._config.grid_options === config.grid_optionsпо идентичности объекта.
- сверка
- AC7 —
ordinaryCardKeepsViewportHeight: true(реальный inline-style100dvhприcard.layout = null);smoke_houseplan_panel.mjs→sectionsDefaultsRemainAvailableInPanel: true(панель не задета рефакторомpanelHost → containerOwnedHeight, поскольку_containerOwnedHeight === panelHost, когдаlayoutне установлен) — прогнан лично, зелёный. - AC8 —
resizePreservesCameraAndIntent: true(0hass-more-info,_space/_modeне смещаются);postResizeHitCoordinatesMatchCamera: true— сверка реальнойgetScreenCTM()после шторма resize. - AC9 —
resizeStormSettlesWithoutLoop: true— 30 переключений 6/14 строк,stormElapsed < 3000;finish()красит прогон при необработанных исключениях карточки (_pageErrors), не только по заявленным булевым полям. - AC10 — все шесть документов обновлены и прочитаны целиком; числа 10/6
и пиксельные ссылки (632/376) существуют только как проза, единственный
источник —
getGridOptions()(см. «Как проверялось» п.10);@pando80кредитован в обоих changelog со ссылкой на #648;check-docs.mjs --screenshots=warn— WARN (не ERROR), не относится к этому дифу. - Трейлеры и changelog:
Issue: #648на всех некод-ревью-документных коммитах;User-Visible: yesтолько на2452c51d, оба changelog — в том же коммите. - Бандл воспроизводим:
npm run build && npm run bundle:syncна материале не породил диффа — все три копии (dist/**,custom_components/houseplan/frontend/**,demo/srv/assets/**) побайтно совпадают с исходниками этого SHA. - Гейты для
src/**-дифа:no-new-any,no-new-private-writes,bundle:budget,validate-commit-provenance— все зелёные (см. «Как проверялось» пп. 5, 9). - Юнит-тесты:
npm test— 3086, 3085 pass, 1 skip (не относится к задаче), 0 fail. - Область изменений:
panel-host, Masonry, kiosk,space-card.tsне тронуты кодом — подтверждено grep и фактическим составом дифа.
Чего не проверял
- Реальное поведение Home Assistant Sections вне этого репозитория.
Как и в r1 — нет ни реального HA, ни доступа к
WebSearch/поиску по коду GitHub в этой среде (инструментыmcp__github__get_issue/get_issue_commentsтоже были недоступны без явного разрешения; тело и комментарии issue получены черезghCLI, который разрешён). AC2–AC10 в этом дифе по-прежнему проверены только против синтетической фикстуры, устанавливающейcard.layout = 'grid'вручную. Новое по сравнению с r1: комментарий@pando80(см. «Как проверялось» п.12) — независимое подтверждение на реальном HA 1.78.0-beta.2 сgrid_options: rows: 6, план не переполняет карточку. Это анекдотическое, не воспроизводимое автоматически свидетельство, но оно прямо адресует технический риск, который ревью ТЗ делегировало код-ревью, и снижает его до низкого: внешний пользователь уже наблюдал ровно тот сигнал (layout), на который рассчитывает код, на реальной установке. - 15 из 19 «прямых совпадений» и все 28 «слабых связей»
smoke-select.mjs. Тот же список, что в r1 (диф с r1 не расширил затронутые символыsrc/**); прогнал только 4 релевантных (smoke_sections_resize.mjs,smoke_houseplan_panel.mjs,smoke_render_invalidation.mjs,smoke_warm_dialogs.mjs) — обоснование то же: рефакторpanelHost → containerOwnedHeightматематически не меняет значение для существующих (не-grid) карточек; полная матрица — предрелизная обязанность (§8), не гейт код-ревью. golden:verify— не прогонял: диф не меняет визуальные пиксели плана/chrome, только контейнерную высоту и CSS-flex;check-docs.mjsподтверждает отсутствие изменений в зафиксированных скриншот-сценариях документации (только WARN о фингерпринте).npm run invariants— не прогонял: диф не меняет геометрическую модель плана и не трогает ссылки на неё, только презентационный слой карточки.python -m pytest tests_backend— не прогонял: диф не касаетсяcustom_components/**/*.py.- Performance-профили — не названы в AC; новый постоянный
observer/polling не введён (используется существующий
_roViewport). - Полная матрица browser smoke в CI (271 смок) — не прогонял целиком, только выбранные по диффу и AC (см. выше); это соразмерно объёму гейтов код-ревью (§8), не предрелизному гейту.
Вердикт
0 High, 0 Medium. Единственная находка предыдущего раунда (Medium-1: неполное
покрытие AC3 браузерным смоком RU/DE) закрыта предметно — исполняемый смок
теперь проверяет минимальную высоту на обеих локалях и был лично прогнан
зелёным. Новых находок при полном разборе (обязателен из-за ребейза на
ушедший вперёд dev) не выявлено: все AC1–AC10, четыре защитных мутанта,
оба относящихся к задаче browser smoke плюс два точечных regression-смока,
трейлеры, оба changelog и гейты, специфичные для src/**-диффа
(no-new-any, no-new-private-writes, bundle:budget,
validate-commit-provenance, check-docs), — лично перегнаны на этом SHA и
корректны.
Материал ревью содержит аномалию бухгалтерии раундов (не найден артефакт r2 —
см. раздел выше); это вопрос целостности конвейера, а не кода задачи, и не
меняет техническую оценку: код на SHA 4168b1cf готов.
Зелёный.
Вердикт: зелёный · заход r3 · блокирующих циклов 2/4 · High: 0 · Medium: 0
Материал раунда
- Ветка: без явного имени в рабочей копии на момент ревью (детектирована
как
HEADdetached); вершина —4168b1cfcedcc7979e17a380427d4466b45f6b2b. - База сравнения:
origin/dev=7372e977b89a0a907b67cf00c1b2cd2b9ed2ddfa. - Дерево материала (
git rev-parse HEAD^{tree}на момент ревью): снято на вершине4168b1cfcedcc7979e17a380427d4466b45f6b2b, рабочая копия чиста до и после всех локальных гейтов/мутаций/пересборки бандла (git status --porcelainпуст). - Предыдущий раунд (найден):
docs/reviews/CODE-REVIEW-648-r1.md, вердиктyellow, High 0, Medium 1 (закрыт — см. таблицу выше). Раунд r2 код-ревью не найден ни вdocs/reviews/, ни в комментариях issue #648, ни в её таймлайне меток — см. раздел «Аномалия материала» выше.
Материал раунда
- Ветка:
issue/648-sections-card-resize, коммит4168b1cfcedc— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
06653e5b743ecdf436899320c19a411f04af6286git log --all --format='%H %T' | grep 06653e5b743e - Тело issue:
8400466150bde494c01aef14b7d5961be36d721b08a28c69e800bd88ac645154 - Вердикт конвейера:
green· High 0