24 KiB
CODE-REVIEW-86-r2
- Issue: #86 — Тексты подсказок к настройкам, партия 1
- ТЗ:
docs/specs/086-settings-help-content-party1.md(актуализировано 2026-08-30, зелёный SPEC-REVIEW-86-r1) - SHA материала:
18ab3779980e1d22f2cdc4a6da12a4ef2c1a4df9(origin/dev...HEAD, 8 коммитов:fa0319205994c929b4c5083f9e559b10be6bd5f64015f7f810f441aa18ab3779) - Трек: полный (нарушен критерий light-track «одна поверхность», зафиксировано автором в шапке ТЗ)
- Заход: r2 · блокирующих циклов израсходовано 1/4 (r1 — красный)
Почему разбор полный, а не по дельте
Между r1 (красный, материал bd27fca1→c862de65) и этим заходом ветка была
перебазирована на ушедший вперёд origin/dev (комментарий автора: «ветка
перебазирована на актуальный dev 8819e390»; конфликт возник в
custom_components/houseplan/frontend/**, dist/**, docs/images/**,
docs/CHANGELOG*.md — то есть в сгенерированных деревьях и релизных
артефактах, задетых параллельно слитой #383). Предыдущие SHA (bd27fca1,
489cc8b6, c862de65, 8633187d, 3274ac39) не существуют в текущей истории
(git rebase их переписал), в чём я убедился лично: git show -s <sha> на них
даёт fatal: ambiguous argument. Это ровно случай PROCESS.md §2.10/§7.2 «ребейз
на ушедший вперёд dev — после ребейза это другой код», поэтому разбираю AC1–AC9
заново, а не только находки r1.
Отдельно: конвейер уже один раз пытался прогнать ревью до этого рейза и
остановился на конфликте («Ревью не запускалось» в issue) — тот заход не
состоялся и бюджет циклов не потратил (в issue это явно записано), поэтому
текущий заход законно r2, а не r3.
Скоуп
Diff добавляет 11 согласованных Party-1 hp-help-подсказок в
houseplan-editor-runtime.ts (масштаб пространства, общий Glow-радиус, режим
заливки, роль источника света — обновлён текст пилота #68, управление другими
источниками, общий/локальный север, общий/локальный фон, стиль стен нулевой
толщины, «Показывать скрытые на плане») и дублирует 5 space-подсказок в
отдельном houseplan-onboarding-runtime.ts; переводит их на en/ru/de/fr;
удаляет 4 старых .rhint; добавляет unit-тесты инвентаря/parity/no-duplicate,
мутационный тест settings-help-party1-placement-removed, расширяет
smoke_help_affordance.mjs cold-onboarding сценарием; правит CHANGELOG×2,
USER-GUIDE.ru.md, TESTING.md, docs/specs/README.md; принимает 6
golden-эталонов (+2 не связанных с #86, для уже слитой #383) и полный
десятикадровый набор docs/images/** после рейза.
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
H1 (High) — docs/images/screenshots.json подделан, PNG не пересняты |
Коммит 18ab3779 заменяет все 10 PNG (байты изменились) и все 10 imageSha256/sourceSha256 в манифесте, oxipng: null → "oxipng 10.2.0" — признак настоящей пересъёмки через docs:accept, а не ручной правки JSON; трейлеры Release:+Baseline-Reviewed: со ссылкой на прогон 33306281294 |
git show 18ab3779 --stat -- docs/images/ (11 файлов, включая JSON); я лично открыл docs/images/03-space-create.png и 06-device-editor.png — на обоих видны новые иконки-подсказки («Scale (grid cell size)» ⓘ, «Zero-thickness walls» ⓘ, «Controls other light sources» ⓘ) |
H2 (High) — golden:verify красный, 6 эталонов не приняты |
Коммит be6bd5f6 принимает ровно эти 6 (device-inbox-desktop-en-light, device-inbox-desktop-ru-dark, device-inbox-narrow-ru-dark, toggle-entity-dialog-desktop-en, toggle-entity-dialog-mobile-ru, space-room-color-popover-desktop-ru) + 2 несвязанных furniture-transform-* для #383, с Release:+Baseline-Reviewed: (прогон 33305056892) |
Я прогнал npm run golden:verify лично на 18ab3779 (полный лог, без truncation) — exitCode=0, все 6 сценариев passed; визуально открыл device-inbox-desktop-en-light.png, toggle-entity-dialog-desktop-en.png, space-room-color-popover-desktop-ru.png — новые ⓘ-иконки на месте, без наложений и обрезки |
| M1 (Medium, в скоупе) — cold-onboarding путь 5 space-подсказок не доказан исполнением | Коммит 10f441aa добавляет в smoke_help_affordance.mjs реальный запуск launchColdView() с пустым _serverCfg.spaces, дожидается _onboardingRuntime && !_editorRuntime, проверяет инвентарь всех 5 space.*.help, открытие одной подсказки, read-only черновика и то, что editor runtime не подгрузился |
Я прогнал node demo/smoke_help_affordance.mjs лично — party1ColdOnboardingInventory/HelpOpens/HelpIsReadOnly/KeepsEditorLazy все true, checkAll требует true по умолчанию (падает при false), OK |
L1 (Low, снят без правки) — .help-ключ не совпадает буквально с ключом label |
Не задета рейзом и правками r1→r2, решение прежнее в силе | space.cell_cm.help рядом с space.scale_label, space.fill_mode.help рядом с space.fill_label — как и в r1 |
Унаследовано из r1
Так как разбор в этом заходе полный (см. выше — ребейз на ушедший вперёд dev), формально всё перепроверено заново на текущем SHA, а не унаследовано без проверки. Из r1 без повторной аргументации взято только:
- сам факт, что механизм
hp-help/hp-dialog(#68) не форкнут и не изменён — перепроверено тем же способом (git diffпоsrc/hp-help*,src/hp-dialog*пуст) и подтверждено заново, а не принято на слово; - список Party-1 из 11 ключей и их каноничные RU/EN тексты — сверены заново
построчно с §6 ТЗ и с тестом
issue 86 Party 1 has the exact help inventory.
Никакой пункт r1 не принят по одному лишь заявлению автора без собственной проверки в этом заходе.
Как проверялось
Дешёвые гейты Validate на 18ab3779 формально зелёные (прогон 33306392784),
но при разборе job'ов выяснилось, что тяжёлые job'ы (Фронтенд: типы, юниты, мутанты, синхрон бандла, Golden-кадры…, Смоки в браузере…) там skipped
(переиспользованы) через scripts/gate-reuse.mjs — то есть Validate НЕ
прогнал их заново на этом SHA, а сверил хеш входов с прошлым успешным прогоном.
Механизм легитимный (ключ = контент-хеш src/**+гарнитура job'а, не имя ветки
или SHA — прочитал scripts/gate-reuse.mjs), но раз я это заметил, не
полагаюсь на устную переиспользованную метку и прогнал сам:
| Гейт | Команда | Результат |
|---|---|---|
| Typecheck | npx tsc --noEmit |
зелёный |
| Unit | npm test |
1611 тестов, 1610 pass, 1 skip (тот же приватный #281-фикстур-скип, что и на origin/dev, к #86 не относится), 0 fail |
| Build + sync | npm run build, npm run bundle:sync |
ок |
| 3 копии бандла | cmp dist/houseplan-card.js custom_components/.../houseplan-card.js, cmp dist/houseplan-assets.json custom_components/.../houseplan-assets.json |
идентичны (третья копия demo/srv/assets пересобрана тем же bundle:sync) |
| Bundle budget | npm run bundle:budget |
initial View 280 304 B гзип, бюджет 300 000 — в норме |
| no-new-any | node scripts/no-new-any.mjs --base origin/dev --head HEAD |
119 добавленных строк в 3 файлах, новых any нет |
| check-docs | node scripts/check-docs.mjs |
зелёный, и в этот раз обоснованно — см. закрытие H1 |
| Выбор смоков | node scripts/smoke-select.mjs --base origin/dev --head HEAD |
12 «прямое совпадение», 18 «слабая связь» |
| Смоки прямого совпадения (12/12) | smoke_help_affordance, 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 зелёные, включая новый cold-onboarding блок |
| Мутационный гейт задачи | node scripts/mutation-gate.mjs --id=settings-help-party1-placement-removed |
мутант пойман |
| Golden | npm run golden:verify (полный лог, без tail) |
exitCode=0, 0 different/missing, все 6 задетых #86 сценариев passed |
| process-gate (офлайн) | node scripts/process-gate.mjs |
пройден, 0 предупреждений (проверка статуса issue пропущена — нет --issues) |
Что не прогонял и почему:
- 18 «слабых связей» из
smoke-select(общее имя_config) — все про геометрию/локали/optimize, diff их логики не касается, сигнала для расширения после 12/12 прямых совпадений нет; python -m pytest tests_backend -q— diff не касаетсяcustom_components/**/*.py;- остальные ~193 браузерных смока и
performance_smoke— задача не задевает геометрию/перф-чувствительные пути, полный прогон здесь избыточен (§8: полные наборы — предрелизный гейт); model-invariants— diff не трогает геометрию, ссылки,layout,marker.space,open_spans; неприменимо.
Находки
M1 — AC7 «browser zoom 200%» не доказан ничем (Medium, в скоупе)
ТЗ §13 формулирует AC7: «Узкий viewport 390 px и browser zoom 200% не обрезают tooltip/trigger и не меняют stage geometry. Доказательство: smoke + representative reviewed golden», а план автотестов §14 отдельно требует golden «light/dark and 200% zoom; full Linux artifact review required».
Проверил оба канала:
grep -n "zoom" demo/smoke_help_affordance.mjs— пусто: файл проверяет только{ width: 390, height: 780 }сdeviceScaleFactor=1(2-й аргументlaunch), то есть узкий viewport, но никакого 200%-zoom;- ни один из 6 принятых в рамках закрытия H2 golden-сценариев
(
device-inbox-*,toggle-entity-dialog-*,space-room-color-popover-desktop-ru, см.demo/golden/matrix.mjs:383-397, 765-778, 795-797) не задаёт zoom-эмуляцию; в матрице вообще нет ни одного сценария, который проверял бы party1-триггеры при 200% browser zoom (large-house-zoom-250-dark— это zoom канвы плана, а не browser-zoom диалога, и не про эту задачу).
Находка r1 упомянула эту часть AC7 мимоходом («200%-zoom и «reviewed golden»
часть AC7 не доказана — см. H2»), но фактическое закрытие H2 (be6bd5f6)
приняло именно и только эти 6 уже существовавших в плане сценариев — ни один
из них 200%-zoom не тестирует, поэтому проблема осталась ровно там же, где
была, просто перестала быть видна за красным golden:verify.
Прочитал добавленный CSS (src/styles/dialogs.styles.ts, новые
.helpfieldlabel, .help-inline-label, .device-inbox-filter-help): все три
класса используют display: flex/inline-flex, flex-wrap: wrap,
min-width: 0 и overflow-wrap: anywhere на вложенном <label> — тот же
паттерн, что и уже принятый в #68 .markerhelplabel. Это делает вероятным, что
разметка действительно не обрезается при увеличении шрифта, но это чтение, а
не исполнение, и для новых host-классов (.helpfieldlabel,
.device-inbox-filter-help) при 200% zoom его никто — ни автор, ни
предыдущий заход — не подтвердил ни автотестом, ни golden-кадром, как того
явно требует собственное ТЗ задачи.
Как воспроизвести: grep -n "zoom" demo/smoke_help_affordance.mjs → 0
совпадений; grep -n "zoom" demo/golden/matrix.mjs рядом со сценариями
device-inbox-*/toggle-entity-dialog-*/space-room-color-popover-desktop-ru
→ 0 совпадений.
Что нужно: либо добавить golden-сценарий(ы) с эмуляцией 200% zoom для
одного-двух party1-диалогов (например page.emulateMedia/увеличенный
fontSize/deviceScaleFactor, как принято в проекте) и smoke-проверку, что
.trigger/label не выходят за visualViewport, либо явно зафиксировать в
ТЗ/тестовом плане и в этом ревью, что 200%-zoom проверяется только моим
чтением CSS — и указать, почему этого достаточно для приёмки. Само по себе
чтение CSS без исполнения я делаю сейчас как ревьюер, но AC7 в ТЗ прямо назвал
другой способ доказательства, который не выполнен.
L1 — .help-ключ не совпадает буквально с ключом видимого label (Low, снимается без правки)
Унаследовано из ревью ТЗ (SPEC-REVIEW-86-r1) и CODE-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 нет. 5space.*-ключей идентичны междуhouseplan-editor-runtime.tsиhouseplan-onboarding-runtime.ts(метод_help()в обоих файлах текстуально идентичен,src/hp-help*не менялся). Доказано тестомissue 86 Party 1 has the exact help inventory(прогнал, зелёный) и мутационным гейтомsettings-help-party1-placement-removed(прогнал лично — мутант пойман). - AC2/AC3 (канонический текст, parity). RU/EN в
en.json/ru.jsonпобайтово совпадают с таблицей §6 ТЗ — сверил вручную и тестомissue 86 Party 1 has the exact help inventory and canonical RU/EN copy(жёсткоеassert.equalпо каждому ключу, прогнал, зелёный). DE/FR прочитаны целиком — передают тот же смысл без пропусков предложений..help.ariaпары присутствуют во всех 4 словарях, тестi18n: every literal help call has body and full aria keys in every languageзелёный (теперь явно проверяет иhouseplan-onboarding-runtime.tsчерез объединённыйhelpSource, а не толькоcardSource, как было в r1). - AC4 (без дублей).
marker.controls_hint,gs.bg_daynight_hint,gs.north_hint,space.zero_wall_helpудалены из всех 4 словарей и обоих рантаймов — тестissue 86 removes explanatory hints…зелёный,grepлично подтвердил отсутствие. Текст пилотаmarker.light_role.helpзаменён каноническим (сверилgit diff— старый текст «Controls whether this marker itself is a spatial light source…» заменён на текст §6 таблицы ТЗ). - AC5 (все поверхности). General (
gs.glow_radius,gs.bg_mode,gs.north), Space (space.cell_cm,space.zero_wall_style,space.bg_mode,space.north,space.fill_mode), Marker (marker.controls.help) и Device catalog (device_inbox.show_hidden.help) — все присутствуют в диффе шаблонов; открытие подтвержденоsmoke_help_affordance(party1GeneralInventory,party1SpaceInventory,party1ShowHiddenHelpIsReadOnlyи т. д. — всеtrue) и визуально — открылdevice-inbox-desktop-en-light.png,toggle-entity-dialog-desktop-en.png,space-room-color-popover-desktop-ru.png,06-device-editor.png,03-space-create.png: везде новые ⓘ-иконки на месте своих полей, без наложений на соседние элементы и без обрезки. - AC6 (input/a11y). Переиспользует lifecycle #68 без форка;
smoke_help_affordance(прогнал, зелёный) покрывает hover/focus/click/Escape/disabled/Popover-fallback, включаяkeyboardFocus: true. - AC7 (узкий viewport 390 px). Часть про 390 px доказана (
party1ShowHiddenHelpInsideNarrowViewportв смоке, узкий goldendevice-inbox-narrow-ru-dark,toggle-entity-dialog-mobile-ru— всеpassed). Часть про 200% zoom — см. находку M1. - AC8 (модель не меняется). Прочитан весь diff
src/**(83+44+33 строк в трёх файлах не считая i18n) — ни одного изменения вне JSX-шаблонов, CSS и_help()-геттеров;strictNumber,touchSpaceDisplay,@change/@inputобработчики не тронуты.npm test(1610/1611 pass) включает существующие config-serialization снапшоты — без регрессий. - AC9 (партии 2/3 отсутствуют).
src/**вне i18n просмотрен построчно — толькоhouseplan-editor-runtime.ts,houseplan-onboarding-runtime.ts,dialogs.styles.ts; ни одного_help()вне §6 сверх уже существующих пилотов; в i18n нет ключейopening.*/wallthick.*/decor.*/device.binding.*партий 2/3. - Одно число — один источник. Diff не добавляет и не меняет ни одной пользовательской величины — только текст подсказок и разметка; правило неприменимо.
- Трейлеры и changelog.
Issue: #86на всех 8 коммитах;User-Visible: yesтолько наb4c5083f, в нём же правки обоих changelog; коммиты, трогающиеdemo/golden/baselines/**(be6bd5f6) иdocs/images/**(18ab3779), несутRelease:+Baseline-Reviewed:со ссылками на конкретные зелёные прогоны.docs/specs/README.mdсодержит строку #86 → файл спеки. - process-gate.mjs (офлайн). Пройден, 0 предупреждений.
Вывод
Все три блокирующих находки r1 (H1, H2, M1) закрыты содержательно — я лично
пересобрал бандл, прогнал golden:verify полным логом (не через tail, чтобы
не потерять реальный код возврата), запустил новый cold-onboarding смок и
открыл сами PNG глазами, а не поверил хендофф-комментариям. Контент-часть
партии 1 (11 подсказок, 4 локали, source contract, mutation-тест) корректна и
без регрессий после рейза.
Новая находка — AC7 в части «browser zoom 200%» так и не получила ни автотеста, ни golden-кадра, ни явной записи «проверено чтением» от автора, хотя ТЗ этой же задачи прямо называет это дважды (AC7 и план тестов §14). Я проверил CSS чтением и паттерн выглядит безопасным (совпадает с уже принятым в #68), но это не заменяет обещанное автором доказательство и не было названо явно нигде в хендоффе. High-находок нет, поэтому вердикт — жёлтый, а не красный: это находка уровня Medium в скоупе задачи, которая чинится в этом же issue.