21 KiB
CODE-REVIEW-86-r1
- Issue: #86 — Тексты подсказок к настройкам, партия 1
- ТЗ:
docs/specs/086-settings-help-content-party1.md(актуализировано 2026-08-30, зелёный SPEC-REVIEW-86-r1) - SHA материала:
bd27fca107f61eee74d69bffeb1e15506402b6a4(origin/dev...HEAD, diff трёх коммитов 97c807ba/58fb2ef5/bd27fca1) - Трек: полный (нарушен критерий light-track «одна поверхность», сам автор это фиксирует в шапке ТЗ)
- Заход: r1 · блокирующих циклов израсходовано 0/4
Скоуп
Diff добавляет 11 согласованных Party-1 hp-help подсказок (масштаб пространства,
общий Glow-радиус, режим заливки, роль источника света — обновлён текст пилота,
управление другими источниками, общий/локальный север, общий/локальный фон, стиль
стен нулевой толщины, «Показывать скрытые на плане») в houseplan-editor-runtime.ts
и параллельно — 5 space-подсказок в houseplan-onboarding-runtime.ts; переводит их
на en/ru/de/fr; удаляет 4 старых .rhint; добавляет unit-тесты инвентаря/parity/
no-duplicate, мутационный тест, один браузерный смок и правки трёх документов
(CHANGELOG×2, USER-GUIDE.ru.md, TESTING.md). Он же трогает
docs/images/screenshots.json (без файлов картинок).
Как проверялось
Прогнано лично на SHA bd27fca1 (окружение содержит Node 22 и Chromium, npm ci
не требовался):
| Гейт | Команда | Результат |
|---|---|---|
| Typecheck | npx tsc --noEmit |
зелёный |
| Unit | npm test |
1607 тестов, 1606 pass, 1 skip (тот же тест, что скипается и на origin/dev, к #86 не относится), 0 fail |
| Build + 3 копии бандла | npm run build затем cmp dist/houseplan-card.js custom_components/.../houseplan-card.js и cmp dist/houseplan-assets.json custom_components/.../houseplan-assets.json |
идентичны |
| Bundle budget | npm run bundle:budget |
initial View 280 313 B гзип, бюджет 300 000 — в норме |
| check-docs | node scripts/check-docs.mjs |
зелёный (но см. находку H1 — зелёный статус здесь ложноположителен) |
| Выбор смоков | node scripts/smoke-select.mjs --base origin/dev --head HEAD |
12 «прямое совпадение», 18 «слабая связь» (см. ниже) |
| Смоки прямого совпадения (12/12) | node demo/smoke_help_affordance.mjs, smoke_bg_color, smoke_color_picker_consumers, smoke_device_inbox, smoke_dialog_zombie, smoke_disabled_device, smoke_esc_dialogs, smoke_gs_always, smoke_ha_controls, smoke_hidden_flag, smoke_motion_sense, smoke_sun |
все 12 зелёные |
| Мутационный гейт задачи | node scripts/mutation-gate.mjs --id=settings-help-party1-placement-removed |
мутант пойман (node --test покраснел, как обязан) |
| Golden | npm run golden:verify |
красный, exit 1 — см. находку H2 |
Что не прогонял и почему:
- 18 «слабых связей» из
smoke-select(общее имя_config) — не прогонял: каждая из них про геометрию/локали/optimize, не про диалоги настроек, диф не касается их логики, и после прогона всех 12 прямых совпадений сигнала для расширения нет. python -m pytest tests_backend -q— не прогонял: diff не касаетсяcustom_components/**/*.py, backend не затронут (среда ревью к тому же не имеет установленного pytest —No module named pytest, что подтверждает отсутствие окружения, а не маскирует красный результат: гейт неприменим по diff).- Остальные 193 браузерных смока — задача не задевает геометрию/логику модели целиком, полный прогон здесь избыточен (PROCESS §8: полные наборы — предрелизный гейт).
model-invariants— diff не трогает геометрию, ссылки,layout,marker.spaceилиopen_spans; не применимо.- Реальный CI Validate на этом SHA не найден зелёным — прогнал дешёвые гейты сам, как и предписано.
Находки
H1 — Манифест скриншотов документации подделан, а не пересчитан (High, в скоупе)
docs/images/screenshots.json в коммите bd27fca1 меняет sourceFingerprint
(d77faa6e… → 401722…) и все десять sourceSha256 на то же новое значение —
но ни один PNG в docs/images/** в этом коммите не изменился
(git show bd27fca1 --stat -- docs/images/ показывает только правку JSON,
0 файлов картинок). Я запустил npm run build и node scripts/check-docs.mjs на
HEAD — гейт зелёный, потому что зафиксированный sourceFingerprint теперь
математически совпадает с текущим src/**. То есть кто-то отдельно посчитал
visualFingerprint() и вписал результат в JSON руками, не выполнив настоящую
пересъёмку.
Это прямо запрещённый в проекте путь. scripts/docs-accept.mjs (единственный
легитимный писатель этого файла) копирует новые PNG из артефакта CI
Docs screenshots и не может обновить манифест без файлов: он сверяет
imageSha256 каждого кадра с байтами кандидата и требует --reviewed.
Исторический прецедент в этом же репозитории — коммиты «docs: refresh screenshot fingerprint» (например b1b1e743) — всегда одновременно переписывают все
десять PNG (см. git show b1b1e743 --stat), потому что даже визуально идентичный
кадр даёт другие байты при пересъёмке (комментарий в docs-accept.mjs объясняет
это явно). Здесь PNG не тронуты вовсе — списанный ярлык не соответствует
содержимому.
Хуже того, содержимое двух сценариев из этого манифеста действительно
устарело: space-create (fixture: 'empty' → card._openSpaceDialog('create')
→ рендерится ровно тот _renderSpaceDialog() из
houseplan-onboarding-runtime.ts, куда этот diff добавил 5 подсказок) и
device-editor (dialog: 'device' → маркер-диалог, где marker.controls_label +
.rhint заменены на helpfieldlabel с новой иконкой) должны показывать новые
значки подсказок — а показывают старую картинку без них. Спецификация §17 прямо
требует «актуальные screenshots» как release-артефакт; вместо этого CI-гейт
docs обманут значением, которое ему не принадлежит.
Как воспроизвести: git show bd27fca1 --stat -- docs/images/ → 0 изменённых
.png; git show b1b1e743 --stat → для сравнения, легитимный рефреш всегда меняет
все 10 файлов.
Что нужно: прогнать GH Actions Docs screenshots (workflow_dispatch),
скачать артефакт, npm run docs:accept -- --reviewed --from=<artifact>, отдать
на ревью человеку и закоммитить обновлённые PNG вместе с манифестом. Понижать
трек до «просто поправить фингерпринт» — то самое сокрытие, которое AGENTS.md
называет запрещённым по отношению к golden-эталонам и что не менее верно для
docs-скриншотов.
H2 — npm run golden:verify красный: 6 эталонов расходятся с рендером (High, в скоупе)
Прогнал npm run golden:verify на bd27fca1: exitCode=1. Расхождения:
different device-inbox-desktop-en-light
different device-inbox-desktop-ru-dark
different device-inbox-narrow-ru-dark
different toggle-entity-dialog-desktop-en
different toggle-entity-dialog-mobile-ru
different space-room-color-popover-desktop-ru
Все шесть напрямую объясняются этим diff'ом, не совпадением имени символа:
device-inbox-*— сценарииdialog: 'device-inbox'(demo/golden/matrix.mjs:383-392) снимают ровно тот блок, где чекбокс «Показывать скрытые на плане» обёрнут в новый<span class="device-inbox-filter-help">с триггеромdevice_inbox.show_hidden.help(houseplan-editor-runtime.ts:11815-11825).toggle-entity-dialog-*— сценарииdialog: 'device'дляgolden-washer(matrix.mjs:765-778) открывают тот же маркер-диалог, гдеmarker.controls_label+ постоянный.rhintзаменены на<div class="helpfieldlabel">с триггеромmarker.controls.help(houseplan-editor-runtime.ts:12588-12592) — сдвиг разметки докатывается вниз доtoggle_entity-секции того же диалога.space-room-color-popover-desktop-ru— сценарийdialog: 'space-room-color'(matrix.mjs:795-797) открывает попап цвета комнаты в диалоге пространства, выше которого теперь стоит новыйhelpfieldlabelдляspace.fill_mode.help.
Ни один demo/golden/baselines/** файл в diff'е не менялся
(git diff --stat origin/dev...HEAD -- demo/golden/ — пусто), значит эти шесть
эталонов не пересматривались вовсе. Хендофф-комментарий автора в issue
(«Проверки: npm test… smoke_help_affordance… mutation… docs и bundle budget — OK»)
golden:verify не упоминает — гейт не прогонялся.
Отдельно (не блокирует и не относится к #86): furniture-transform-light/dark —
missing-baseline, но baseline для них отсутствует в дереве и на origin/dev тоже
— унаследованный, не внесённый этим diff'ом пробел, к задаче не относится.
Как воспроизвести: npm run bundle:sync && npm run golden:verify на
bd27fca1 → EXIT=1, шесть different.
Что нужно: просмотреть artifacts/golden/diff/{device-inbox-*, toggle-entity-dialog-*, space-room-color-popover-desktop-ru}.png, убедиться, что
новая раскладка — то, что задумано (правдоподобно, раз это ровно новые триггеры
подсказок), и принять через npm run golden:accept -- --reviewed на полном
Linux CI-артефакте. ТЗ §14 прямо планировал «Golden: general settings and space
dialog with help open… full Linux artifact review required» — этот пункт плана
не выполнен, а не просто пропущен по недосмотру мелочи.
M1 — Onboarding-путь 5 space-подсказок не доказан исполнением (Medium, в скоупе)
ТЗ §8 требует: «Пять space-подсказок присутствуют и в обычном editor runtime, и в
отдельном onboarding runtime… их текст и поведение идентичны», а §14 планирует
браузерный смок «по одному placement на… space». demo/smoke_help_affordance.mjs
целиком работает через обычный редактор (card._openSpaceDialog('edit', card._space)),
и ни один смок/golden не открывает cold-onboarding путь
(_openSpaceDialog('create') при пустом _serverCfg.spaces), который ведёт в
HouseplanOnboardingRuntime._renderSpaceDialog() с отдельной, скопированной
реализацией _help() (houseplan-onboarding-runtime.ts:54-60). Единственная
проверка, которая реально открывала бы этот путь в браузере — сценарий
space-create документационных скриншотов — сам оказался несвежим (H1).
Статический regex-тест в test/i18n.test.mjs (helpSource = конкатенация двух
файлов) доказывает только, что строка this._help('space.xxx.help') присутствует
в исходнике houseplan-onboarding-runtime.ts, а не что она реально рендерится,
не мутирует _spaceDialog и работает мышью/клавиатурой/тапом в этом отдельном
компоненте.
Прочитал код: _help() в онбординге текстуально идентичен варианту в
houseplan-editor-runtime.ts, импорт ./hp-help добавлен, сигнатура и порядок
аргументов совпадают — на вид рабочий. Но это «проверено чтением, не
исполнением», а не автотест, и явно заявленного как такового в
docs/reviews/handoff нет. Учитывая, что H1 обесценил единственный кандидат на
исполняемое доказательство, AC5/AC7 для onboarding-поверхности сейчас не доказаны
никак, кроме моего чтения.
Что нужно: либо добавить в demo/smoke_help_affordance.mjs (или отдельный
файл) сценарий, открывающий _openSpaceDialog('create') из пустого состояния и
повторяющий read-only/inventory проверки для всех пяти onboarding-триггеров, либо
явно зафиксировать в ТЗ/тестовом плане, что onboarding-путь покрывается только
docs-скриншотом — и тогда почему это допустимо. Совместно с H1 естественно
чинится одним заходом: настоящая пересъёмка space-create подтвердит хотя бы
рендер, а отдельный смок — поведение.
L1 — .help-ключ не совпадает буквально с ключом видимого label (Low, снимается без правки)
Унаследовано из ревью ТЗ (SPEC-REVIEW-86-r1): space.cell_cm.help соседствует с
space.scale_label, space.fill_mode.help — с space.fill_label. Не создаёт
двусмысленности контракта, тот же паттерн уже был у пилотов #68, находится в
рамках технической свободы реализации по PROCESS.md §7.1. Оставляю без правки.
Что проверено и корректно
- AC1 (инвентарь).
grep -oE "_help\('[^']+\.help'\)"по обоим рантаймам даёт ровно 11 Party-1 ключей плюс уже существующие 8 пилотныхmarker.*ключей из #68 — новых мест за пределами §6 нет. Пятьspace.*-ключей продублированы один-в-один вhouseplan-onboarding-runtime.ts. Доказано и чтением, и тестомissue 86 Party 1 has the exact help inventory(test/i18n.test.mjs) — тест запускался (npm test, зелёный) и умеет падать: мутационный гейтsettings-help-party1-placement-removedподтверждает (прогнал лично). - AC2/AC3 (канонический текст, parity). RU/EN тексты в
en.json/ru.jsonпобайтово совпадают с таблицей §6 ТЗ — сверил вручную все 11 строк. DE/FR прочитаны целиком (см. выдержку выше) — передают тот же смысл, не пропускают предложений..help.ariaпары присутствуют во всех 4 словарях, тестi18n: every literal help call has body and full aria keys in every languageпрогонялся и прошёл. - AC4 (без дублей).
marker.controls_hint,gs.bg_daynight_hint,gs.north_hint,space.zero_wall_helpудалены из всех 4 словарей и из обоих рантаймов — подтверждено тестомissue 86 removes explanatory hints…(прошёл) иgrepлично. - AC6 (input/a11y). Переиспользует lifecycle #68 без форка
(
import './hp-help', тот же_help()контракт);smoke_help_affordance.mjs(прогнал, зелёный) покрывает hover/focus/click/Escape/disabled/Popover-fallback для нового содержимого партии наравне со старым пилотом. - AC7 (узкий viewport 390 px).
party1ShowHiddenHelpInsideNarrowViewportв смоке проверяет геометрию попапа в 390 px — прошло. 200%-zoom и «reviewed golden» часть AC7 не доказана — см. H2. - AC8 (модель не меняется). Прочитан весь diff
src/**— ни одного изменения вне JSX/CSS шаблонов и двух новых_help()-методов;strictNumber,touchSpaceDisplay, обработчики@change/@inputне тронуты.npm test(1606/1607 pass) включает существующие config-serialization снапшоты — без регрессий. - AC9 (партии 2/3 отсутствуют). Diff
src/**просмотрен построчно — нет ни одного_help()вне §6 сверх уже существующихmarker.*пилотов; в i18n-файлах нет добавленных ключейopening.*,wallthick.*,decor.*,device.binding.*и т. п. из партий 2/3. - Гейты общего назначения. typecheck/test/build/bundle-sync/bundle-budget/ check-docs (формально) — все зелёные, три копии бандла идентичны.
- Одно число — один источник. Diff не добавляет и не меняет ни одной пользовательской величины (только текст подсказок) — правило неприменимо.
- Трейлеры.
Issue: #86на всех трёх коммитах;User-Visible: yesвbd27fca1— оба changelog (docs/CHANGELOG.md,docs/CHANGELOG.ru.md) правлены в этом же коммите, формулировки на месте.
Вывод
Контент-часть партии 1 (11 подсказок, переводы, source contract, mutation-тест)
выполнена аккуратно и проходит собственные unit- и smoke-гейты. Но два
предрелизных для видимого рендера гейта, обязательных именно для этой задачи —
docs-скриншоты и golden — либо подделаны (H1), либо действительно красные и
не прогонялись (H2). Оба находятся в скоупе issue (сам diff их и ломает) и
требуют настоящей пересъёмки/приёмки, а не текстовой правки. Пока это не сделано,
вердикт не может быть даже жёлтым.