17 KiB
Code review #219 — r1
- Issue: https://github.com/Matysh/houseplan-card/issues/219
- Spec:
docs/specs/219-lock-orange-palette.md, зелёноеSPEC-REVIEW-219-r2.md - Reviewed branch:
issue/219-lock-orange-palette - Reviewed range:
origin/dev..HEAD=28d6b9c…84fa434(ТЗ и ревью ТЗ, уже приняты на этапе spec) →bc75e00(единственный продуктовый коммит) - Base:
origin/devatb56e122 - Reviewer: Claude, независимая сессия без контекста реализации
Вердикт
Зелёный · цикл r1/4 · High: 0 · Medium: 0.
Единственный продуктовый коммит bc75e00 заменяет старую black/amber
lock-палитру на red/green и унифицирует foreground всех цветных подложек
(on/open) по теме. Изменение точечное и декларативное — только
src/styles.ts — и покрыто исполняемыми contract-тестом и browser-smoke,
которые я лично прогнал, а contract-тест дополнительно проверил на
способность падать. Все 7 AC закрыты либо автотестом, либо чтением кода с
явной пометкой. Одна Low-находка снята без правки.
Скоуп
Продуктовый файл — src/styles.ts:
.oplock.locked/.oplock.unlocked(compact opening lock badge) и.dev.lock-locked/.dev.lock-unlocked(обычный marker) получили core/stroke#66D17A/#F0410Cвместоlight-dark(#000,#252525)/#F0A00C; foreground переведён на общий паттернlight-dark(#fff,#252525)+ explicit.theme-light/.theme-darkoverrides, как уже сделано у.dev.on;.dev.openполучил тот же foreground-паттерн вместо постоянного#4a2800, плюс явные.dev.theme-light.open/.dev.theme-dark.open;- ни geometry, ни stroke-ratio, ни selector order, ни alarm/hover/focus/
selected/unavailable/virtual/press-правила не тронуты — весь остальной файл
вне двух изменённых блоков идентичен
dev.
Сопутствующие изменения: test/device-marker-polish-contract.test.mjs (новый
contract-тест issue 219), demo/smoke_device_icon_design.mjs (новый
openingLockPaletteMatches/orangeGlyphsFollowTheme/
darkLockUsesGreenCoreAndDarkGlyph, обновлённый reference override),
demo/capture_device_icon_reference.mjs + demo/srv/reference/device-icons/README.md
(runtime-override red/green поверх byte-identical designer SVG),
demo/golden/matrix.mjs + test/golden-matrix.test.mjs (сцена
device-icon-state-table-* теперь показывает open рядом с on и обоими
lock-состояниями), docs/TESTING.md, docs/USER-GUIDE.md +
docs/USER-GUIDE.ru.md (раздел «Замок» и таблица постоянных статусов —
заменяемый black/amber текст убран), оба changelog, docs/specs/README.md,
docs/images/screenshots.json (обновлён только sourceFingerprint, все 10
imageSha256 без изменений), три сгенерированные копии бандла.
Ровно один продуктовый коммит bc75e00, трейлеры Issue: #219 /
User-Visible: yes на месте, оба changelog в том же коммите, документация
(USER-GUIDE, TESTING) в том же коммите, что и поведение. Ветка
issue/219-lock-orange-palette соответствует правилу именования.
Как проверялось
| Гейт | Результат |
|---|---|
npx tsc --noEmit |
pass |
npm test |
962/962 pass, 0 skipped в этом окружении (npm run inventory подтверждает канонический счётчик 962 Node unit); хендофф указывает «961 passed, 1 skipped» — расхождение объяснимо test/process-gate.test.mjs, где 1 подтест условно t.skip('нужен исполняемый stub gh')/«git недоступен» в зависимости от окружения автора, файл не затронут этим диффом |
npm run build + сверка трёх копий бандла |
pass, все три sha256 = 9c7df844ce825c0d56cfb65e82a0fbbb98620428d5aa5806abec0c40b1d9b1a2, совпадает с хендоффом |
node --test test/device-marker-polish-contract.test.mjs (targeted contract, назван в AC1/AC2/AC3) |
pass (8/8) |
Временный откат --oplock-core-bg: #66D17A → #000 и повтор того же теста |
тест issue 219 gives locks red-open green-closed… красный — дисциплина «тест умеет падать» подтверждена мной, а не только на слово автора |
node --test test/golden-matrix.test.mjs |
pass (26/26), включая новую проверку deviceClassOverrides['golden-right-linkquality'] === ['open'] |
node demo/smoke_device_icon_design.mjs (назван в AC1/AC3, хендоффе) |
pass, все поля true, включая новые openingLockPaletteMatches, orangeGlyphsFollowTheme, darkLockUsesGreenCoreAndDarkGlyph |
node demo/smoke_device_preview_parity.mjs (поверхность AC5 — Device preview) |
pass |
node demo/smoke_static_icon.mjs (поверхность AC5 — static space card) |
pass |
node demo/smoke_lock_action.mjs (смежная поверхность — lock action не должен был измениться) |
pass |
node demo/smoke_lock_invariant.mjs (смежная поверхность — lock invariant/SCOPE.md guard) |
pass |
node scripts/check-docs.mjs --external (AC7) |
pass, 7 файлов, 10 внешних ссылок |
npm run golden:verify |
device-icon-state-table-light/-dark — different, ожидаемо (см. ниже); плюс 3 сцены, не названные в ТЗ — разобраны отдельно |
Разбор golden:verify: почему «different» больше, чем в ТЗ
ТЗ (§10, §15, §16) называет ревьюеру только device-icon-state-table-light/
-dark как ожидаемую визуальную дельту. Фактический прогон показал ещё три
«different»: isometric-large-warm-remount-dark, large-house-zoom-250-dark,
large-house-warm-remount-dark (у всех пяти одинаковый maxObservedDelta: 218
— общий признак одного и того же цветового изменения).
Я не принял это на слово и сравнил с чистым origin/dev в отдельном
git worktree (та же сборка, тот же golden:verify): на dev эти три сцены
проходят как passed, а device-icon-state-table-* уже были different/
missing-baseline до этой задачи — предсуществующий дрейф baseline
(GOLDEN_MATRIX_VERSION 32→33 внесён более ранним 39456dc, эталоны не
приняты), не имеющий отношения к #219. Причина трёх новых «different» —
demo/fixtures/large-house.mjs строка 129 циклически включает
['lock', 'locked'] в синтетический набор устройств, которым пользуются
perf-floor-1/perf-floor-2; палитра lock-marker в этих сценах меняется тем
же общим CSS, что и ожидалось для shared renderer (AC5). Это не регрессия и
не «постороннее» изменение из риска в §13 — только более широкий, чем
задокументировано, набор golden-сцен, которым в предрелизном гейте потребуется
npm run golden:accept -- --reviewed вместе с state-table.
device-text-shell-long-light/-dark — missing-baseline независимо от этой
задачи (тот же предсуществующий дрейф dev, issue #217, вне скоупа #219).
Не прогонялось, и почему
- Полный набор из 155 browser-smoke. Задача — точечное изменение общих CSS state-токенов; прогнаны названные в AC/хендоффе плюс смежные поверхности (device preview parity, static card, lock action, lock invariant). Остальные 150 смоков не относятся к lock/orange-подложке (проёмы без замков, Glow, wall geometry, UI-хром и т. д.).
python -m pytest tests_backend. Ни один файлcustom_components/**/*.pyне тронут.- Performance-профили. Не названы в AC; §12 ТЗ явно фиксирует «CSS variables и fixture/test metadata», без нового DOM/JS в render path.
Проверка AC1–AC7
| AC | Метод по ТЗ | Статус | Как закрыт |
|---|---|---|---|
| AC1 | contract unit + light/dark visual artifact | ✅ | device-marker-polish-contract.test.mjs проверяет .oplock.locked/unlocked core/stroke/fg по обеим темам; artifact — smoke_device_icon_design.mjs openingLockPaletteMatches (реальный .oplock-core/.oplock-shell computed style против reference с runtime-override) |
| AC2 | presentation/unit source contract + shared-renderer test + golden state-table | ✅ | Тот же contract-тест проверяет .dev.lock-locked/unlocked идентичными hex теми же селекторами, что и .oplock.*; houseplan-card.ts/hp-device-preview.ts/space-card.ts подтверждены источником как использующие общий cardStyles; golden device-icon-state-table-* включает оба lock-состояния |
| AC3 | contract unit + state-table light/dark golden с одновременно видимыми on/open |
✅ | Contract-тест проверяет .dev.open не содержит #4a2800 и использует light-dark(#fff,#252525); smoke orangeGlyphsFollowTheme подтверждает rgb(255,255,255)/rgb(37,37,37) для on и open в обеих темах на реальном рендере; golden-сцена теперь содержит open (golden-right-linkquality) рядом с on |
| AC4 | существующие device presentation/polish/pointer tests + source review | ✅ (чтением) | Diff src/styles.ts ограничен двумя блоками (oplock.locked/unlocked, dev.open/lock-locked/lock-unlocked); alarm/hover/focus/selected/unavailable/virtual/press/pulse селекторы вне диффа побайтово идентичны dev; полный regression (962/962) не покраснел |
| AC5 | shared-renderer unit + golden matrix | ✅ | Contract-тест проверяет, что houseplan-card.ts, hp-device-preview.ts, space-card.ts импортируют cardStyles; smoke smoke_device_preview_parity.mjs/smoke_static_icon.mjs подтверждают parity на реальных DOM для plan/preview/static; golden device-icon-state-table-{light,dark} — одна fixture, обе темы |
| AC6 | diff review, typecheck, полный unit и build | ✅ | Diff не касается config/backend/i18n (нет файлов вне src/styles.ts, тестов, demo, docs, generated); typecheck/unit/build зелёные |
| AC7 | diff, hash comparison, check-docs |
✅ | Оба changelog, docs/TESTING.md, docs/USER-GUIDE.md+.ru.md (раздел «Замок», таблица «Жёлтая подложка»/«Чёрный значок замка» переписаны на red/green) — все в bc75e00; три bundle sha256 идентичны; check-docs.mjs --external pass; sourceFingerprint в docs/images/screenshots.json обновлён и совпадает с фактическим build fingerprint, все 10 imageSha256 не изменились |
Находки
Low-1 — AC7/§16 не называют все golden-сцены, которые изменит эта правка
ТЗ и хендофф называют только device-icon-state-table-light/-dark как
ожидаемую golden-дельту. Фактически общий CSS задевает ещё три сцены
(isometric-large-warm-remount-dark, large-house-zoom-250-dark,
large-house-warm-remount-dark) через lock-устройства в синтетической large
fixture (см. разбор выше) — я подтвердил это сравнением с чистым dev, а не
предположением. Риск §13 «Golden покажет ожидаемую дельту вместе с
посторонней» ровно это и предвидел, возлагая проверку на ревьюера — что я и
сделал.
Вердикт: снимается без правки. Не блокирует AC7 (диапазон изменяемых
сцен явно не был ограничен списком) и не создаёт риска для релиза — я
проверил происхождение всех пяти «different» и подтвердил, что это одно и то
же корректное цветовое изменение, а не регрессия. Фиксирую для предрелизного
гейта: npm run golden:accept -- --reviewed должен принять все пять
сцен разом (device-icon-state-table-{light,dark} +
isometric-large-warm-remount-dark + large-house-zoom-250-dark +
large-house-warm-remount-dark), не только две названные в ТЗ.
device-text-shell-long-{light,dark} (missing-baseline) — предсуществующий
дрейф dev вне скоупа #219, не путать с этой правкой при приёмке.
Что проверено и корректно
.oplock.locked/.oplock.unlockedи.dev.lock-locked/.dev.lock-unlockedиспользуют идентичные hex (#66D17A/#F0410C) и идентичныйlight-dark+explicit-override паттерн foreground — одна lock-сущность даёт одинаковый цвет как compact badge, так и обычный marker (риск §13 «Compact opening badge и device marker разойдутся» закрыт одним contract-тестом на обе пары).- Исходные designer SVG (
demo/srv/reference/device-icons/**) остаются byte-identical; runtime-override вcapture_device_icon_reference.mjsподтверждён построчным чтением фактическихfill="black"/fill="#252525"/fill="#1DC21D"/fill="#F0A00C"— каждое встречается в SVG ровно один раз,replaceAllне задевает ничего постороннего. --oplock-stroke-ratio: .025/--device-shell-stroke-ratio: .025(dark theme) и вся геометрия/MDI-path/hit-area не изменены — совпадает с «не входит в задачу» §7 ТЗ.mdi:lock/mdi:lock-open-variantclassification не менялась (regex-проверка в contract-тесте противhouseplan-card.ts).docs/USER-GUIDE.md/.ru.md: заменяемые формулировки («чёрный/тёмный замок», «жёлтая подложка» применительно к lock, «Чёрный значок замка») убраны полностью, включая таблицу постоянных статусов иlock.*в общей таблице поведений — сверено построчно, не на слово хендоффа.- Trailers, changelog RU/EN,
docs/specs/README.md— все в одном продуктовом коммите, соответствуют PROCESS.md §7.1/§10.1.
Чего не проверял
- Визуальный итог обновлённых golden-сцен (baseline не принимается
исполнителем/ревьюером по контракту — только
npm run golden:accept -- --reviewedна полном Linux CI artifact перед бетой). - Полный набор из 155 browser-smoke и
performance_smoke— не относятся к этому точечному CSS-изменению; остаются обязательными на предрелизном гейте. - Backend/HA harness — не затронут, ни один файл
custom_components/**/*.pyне изменён. - Мобильный/touch путь отдельно не тестировал: правка ограничена CSS-цветом,
не добавляет и не убирает pointer-modality селекторы (подтверждено чтением
диффа — новых
:hover/[data-pointer-*]правил нет).