12 KiB
CODE-REVIEW-664-r1
Issue: #664 · этап: code · заход: r1 · материал: 40b73cc5999be6fc8f06eb5cac63f74e6c41c4f6
(1 коммит поверх origin/dev 6cdcb1ea) · трек: trivial, короткий · блокирующих циклов
использовано 0 из 2 (бюджет тратят только жёлтый/красный, а этот вердикт зелёный).
Скоуп
Регрессия #564 (v1.76.0-beta.2): общие стили маркеров открывают
pointer-events: auto на .dev::before (44 px пол) и .device-shell-frame
(капсула) — для интерактивного плана это нужно, но статическая карточка
houseplan-space-card импортирует те же стили целиком, и потомки внутри
.hp-static-stage (которая объявлена pointer-events: none) снова стали
целью хит-теста: курсор-«рука» и клики, уходящие в никуда, на контрактно
read-only поверхности.
Работа обслуживает J1/J3 (docs/SCOPE.md) косвенно — не ломает их, а убирает
ложное приглашение к действию там, где действия по контракту нет; это
починка нарушенного UX-контракта, а не новая функциональность.
Диапазон: src/space-card.ts, demo/smoke_space_card.mjs,
scripts/mutation-registry.mjs, docs/ARCHITECTURE.md, docs/CHANGELOG.md,
docs/CHANGELOG.ru.md, docs/images/screenshots.json (только отпечаток).
7 файлов, +95/−14 — совпадает с хендоффом.
Как проверялось
Прочитан весь дифф построчно (git diff origin/dev...HEAD), тело issue,
оценка владельца и хендофф автора. Правило .hp-static-stage *, *::before, *::after { pointer-events: none } (src/space-card.ts:952-963)
разобрано по каскаду: специфичность .hp-static-stage *::before и
.dev::before из devices.styles.ts:185 равна (0,1,1) — как и
.hp-static-stage * против .device-shell-frame (0,1,0) — побеждает
источник, идущий позже в массиве static styles = [cardStyles, css\...`](Lit сохраняет порядок вставки правил,cardStyles` первый); значит новое
правило переопределяет оба opt-in на равной специфичности не случайно, а по
объявленному порядку каскада. Проверено чтением, подтверждено
исполнением (ниже).
Проверено исполнением (не только чтением):
npm run bundle:sync— сборка чистая (tsc + rollup + bundle-sync).node demo/smoke_space_card.mjsна материале — зелёный,markerProbe.insideStage=false,stageGridHitsInside=0/63,stageGridPointerCursors=0— совпадает с числами из хендоффа.- Мутация вручную: вырезал правило
.hp-static-stage *, *::before, *::after {...}изsrc/space-card.ts, пересобрал, прогнал тот же смок —FAIL space-card smoke,markerProbe = {insideStage: true, element: "span.device-shell-frame", cursor: "pointer"},stageGridHitsInside=1— дословно совпадает с заявленным в таблице AC «чем краснеет». Файл восстановлен, пересобран (git status— чисто). node scripts/mutation-gate.mjs --check— весь реестрok, включая новую записьstatic-card-descendants-hit-testable(это то же самое, что пункт 3, но через штатный гейт, а не только вручную).node scripts/smoke-select.mjs --base origin/dev --head HEAD— «НЕОПРЕДЕЛЁННОСТЬ», 0 символов проекта на изменённых строках — совпадает с тем, что назвал автор; связанный смок (smoke_space_card) назван и прогнан отдельно (пункты 2-3).node scripts/check-docs.mjs— passed (дифф трогаетsrc/**).- Разобрана таблица AC/доказательство/мутация из хендоффа автора — числа и
имена элементов (
span.device-shell-frame,div.hp-static-body) совпадают с моим независимым прогоном пункта 3, это не переписанное заявление автора, а воспроизведённый результат. - Проверено (чтением), что фикстура смока не задевает AC1 в части
пылесоса:
spaceId = window.__card._model[0].id— первое пространство (f1);d_mower(единственное устройство-пылесос демо-конфига,demo/srv/demo.html) размещён в пространствеgarden, а неf1. Условие «при наличии пылесоса в фикстуре» не наступает для карточки, которую рендерит этот смок — пропуск отдельной проверки пылесоса не является пробелом AC1, а корректным отсутствием применимого случая. - Проверено (чтением), что
_renderRecoveryOverlay()— соседstageвнутри.hp-static-body, а не потомок.hp-static-stage(src/space-card.ts:914-917), поэтому новое правило.hp-static-stage *не может случайно закрыть интерактивность overlay восстановления, если он когда-либо получит собственные контролы. - Проверено (чтением)
git diffнаdocs/images/screenshots.json: из 11 изменённых блоков во всех меняется толькоsourceSha256/sourceFingerprint(хеш собранного бандла), ни одна строкаimageSha256(хеш самого PNG) не входит в добавленные/удалённые строки диффа — 11 кадров действительно попиксельно идентичны, как заявлено. - Трейлеры коммита:
Issue: #664,User-Visible: yes; оба changelog (docs/CHANGELOG.md,docs/CHANGELOG.ru.md) правятся в этом же коммите — условие §3 п.10 выполнено. - Единственное видимое пользователю число в дифф-тексте (Changelog) —
ссылка на версию
v1.76.0-beta.2, это ссылка на существующий релиз, не новое число с двумя источниками; отдельного «одно число — один источник» риска в этом диффе нет.
Дешёвые гейты (typecheck/test/build+bundle-policy) не перегонялись отдельно:
подтверждены зелёным Validate на этом же SHA
(https://github.com/Matysh/houseplan-card/actions/runs/36234983969), что
совпадает с независимым прогоном npm run bundle:sync (build+tsc) в пункте 1.
Находки
Нет ни одной находки High или Medium. Дифф закрывает ровно заявленный дефект,
не трогает интерактивную карточку (#564 там не отменяется), контракт
docs/ARCHITECTURE.md обновлён вместе с кодом, оба changelog присутствуют,
защитный AC3 доказан таблицей с непустым третьим столбцом и я независимо
воспроизвёл его результат.
Что проверено и корректно
- AC1 — ничто в
.hp-static-stageне цель хит-теста: воспроизведено (elementFromPointпо маркеру и сетке 9×9 не находит ничего внутри сцены). - AC2 — курсор не
pointer, кнопка кликабельна: воспроизведено (cursor: autoна цели хит-теста,deepLinkсодержит#space=, было и раньше). - AC3 — мутант красит AC1/AC2: воспроизведено вручную и штатным
mutation-gate.mjs, результат совпадает с заявленным дословно. - Каскад CSS выбирает нужное правило по source order при равной специфичности — разобран по коду, не только принят на слово.
- Область поражения — только статическая карточка; полная карточка
(
#564) не затронута дифф-текстом за пределамиsrc/space-card.ts. - Документация (
ARCHITECTURE.md) и оба changelog обновлены в одном коммите с трейлерамиIssue/User-Visible. check-docs.mjsзелёный.- Скриншот-снапшот — только отпечаток источника, изображения не менялись.
Чего не проверял
- Полную матрицу смоков,
golden:verify, perf-профили,pytest tests_backend— не названы в AC, диффа Python нет, перф не задет; это совпадает с тем, что назвал автор в разделе «не прогонял», и я с этим согласен: изменение — CSS-правило одной поверхности плюс тест, который его ловит. npm testполным прогоном (всеnode --test test/*.test.mjsчасти) иnpm run buildсо сверкой трёх копий бандла отдельно от Validate — принял зелёный статус Validate на этом же SHA как подтверждение (ссылка выше);npm run bundle:sync, который я прогнал сам, включает build+tsc и является независимым частичным повторением.- Ручное тестирование в браузере вне playwright-смока (реальный HA,
реальный тачскрин) — вне рамок код-ревью на коротком треке; смок
elementFromPoint— адекватная замена для контракта read-only-курсора, сам провёл его дважды (материал + мутация).
Вывод
Все три AC доказаны исполнением, а не только заявлением автора; защитный AC3 закрашен воспроизведённой мутацией с совпадающими деталями. Скоуп, трейлеры, changelog, документация архитектуры и мутационный реестр — на месте. Находок нет.
Вердикт: зелёный.
Материал раунда
- Ветка:
issue/664-static-card-cursor, коммит40b73cc5999b— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
5086e070529bcf9e4aecf4936e3a68185f7b4385git log --all --format='%H %T' | grep 5086e070529b - Тело issue:
02dc2f7bc5b0b56b6f6e007b39eeae8b17b7bef2ec66a7a1a8461dea63435dd3 - Вердикт конвейера:
green· High 0