Files
houseplan-card/docs/reviews/CODE-REVIEW-86-r4.md
T
2026-08-30 14:49:33 +03:00

153 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CODE-REVIEW-86-r4
- **Issue:** #86 — Тексты подсказок к настройкам, партия 1
- **ТЗ:** `docs/specs/086-settings-help-content-party1.md` (актуализировано этим же раундом, п.14 «Golden»; зелёный SPEC-REVIEW-86-r1)
- **SHA материала:** `7408f8afbfc86471c266e4728be5970a4b378b76` (`origin/dev` = `d14cf769`, диапазон не переписан рейзом)
- **Заход:** r4 · блокирующих циклов израсходовано 3/4 (r1 — красный, r2 — жёлтый, r3 — жёлтый)
- **Объём разбора:** по дельте (см. ниже)
## Почему разбор по дельте, а не заново
Между документом r3 (`96bd3e3e`) и текущим HEAD (`7408f8af`) веток не
переписывал ребейз: `git merge-base origin/dev HEAD` == `git rev-parse
origin/dev` == `d14cf769` — та же база, что была у r3, рейза не было. Дельта —
ровно 3 коммита (`git log --oneline 96bd3e3e..HEAD`): `983533a9` (сам
документ r3, не продуктовое изменение), `769cc13d` и `7408f8af`.
`git diff 96bd3e3e..HEAD -- . ':!docs/reviews'` касается только: файлов
golden-конвейера (`demo/golden/{matrix,harness,run}.mjs`), двух новых PNG
эталонов, `baselines-index.json`, `docs/images/screenshots.json` +2 PNG,
`docs/specs/086-…md`, `test/golden-matrix.test.mjs`, и производных бандл-деревьев
(переименование хешированных файлов из-за смены встроенного
`sourceFingerprint`). `git diff 96bd3e3e..HEAD -- src/ custom_components/houseplan/*.py
tests_backend/` — пусто: ни строки продуктового кода, ни бэкенда дельта не
трогает. Это ровно находка r3 M1 (golden-канал для AC7 «browser zoom 200%») и
её механическое следствие (обязательная пересъёмка docs-скриншотов из-за
сдвига общего `sourceFingerprint`), новой подсистемы не задето, контракт
поведения не менялся. Разбираю AC7 заново по существу дельты и наследую
остальные AC из r3 без повторного пересчёта — условия PROCESS.md §2.9
выполнены: дельта локальна и сопоставима по объёму с самой находкой, которую
закрывает.
## Скоуп дельты
- `769cc13d` — добавляет два golden-сценария `settings-help-zoom-200-en-light`
/ `settings-help-zoom-200-ru-dark` (390×900 CSS px, `deviceScaleFactor: 2`)
в `demo/golden/matrix.mjs` (`GOLDEN_MATRIX_VERSION` 52→53); учит
`harness.mjs` открывать `general-help` диалог и явно проверять, что trigger
и tooltip остаются в viewport и у диалога нет horizontal overflow; учит
`run.mjs` поднимать по браузеру на каждый уникальный `deviceScaleFactor`
сценариев вместо одного общего; добавляет `test/golden-matrix.test.mjs`
проверку состава новых сценариев; правит §14 ТЗ, описывая контракт.
- `7408f8af` — принимает два новых PNG-эталона (reviewed) и обновляет
`docs/images/screenshots.json` + 2 PNG, чья пиксельная сумма сдвинулась
из-за смены `sourceFingerprint` (тот же хеш встроен и в docs, и в golden
корпус — правка `harness.mjs`/`matrix.mjs`/`run.mjs` меняет его для обоих).
## Закрытие раунда r3
| Находка r3 | Чем закрыта | Где это видно |
|---|---|---|
| **M1** (Medium, в скоупе) — ТЗ §13/§14 требует для AC7 два отдельных канала доказательства («smoke + representative reviewed golden»); смок был закрыт в r3, golden-кадр так и не появился ни в r2, ни в r3 | **Закрыта полностью.** `769cc13d` добавляет ровно то, что r3 требовал текстом «Что нужно»: `deviceScaleFactor: 2`, viewport 390 CSS px, light+dark, `helpTextRegion` семантическая проверка, `npm run golden:accept -- --reviewed` со ссылкой на прогон. Проверил лично трояко: (1) прочитал assertion в `harness.mjs` — бросает, если `aria-expanded != 'true'`, trigger/surface вне viewport или `dialog.scrollWidth > dialog.clientWidth + 1`, это не тавтология; (2) открыл оба принятых PNG (`demo/golden/baselines/settings-help-zoom-200-{en-light,ru-dark}.png`, 390×900) — на обоих виден открытый попап `gs.bg_mode.help` («Follows the Sun» / «Следует за Солнцем»), диалог целиком в кадре, переполнения нет; (3) вытащил лог job `Golden-кадры против принятых эталонов` прогона Validate на этом же SHA (databaseId `99251300227`, run `33309242147`) — оба новых сценария в списке `passed`, из 147 сценариев ни одного `different`/`missing`, и ключ переиспользования golden в этом прогоне — `cache not found` (job `Переиспользование`, шаг «Маркер golden»), то есть это не переиспользованный старый результат, а настоящее исполнение на `7408f8af`. |
## Унаследовано из r3
Разбор по дельте (см. выше), поэтому AC1–AC6, AC8, AC9 не пересчитывались
заново — дельта их не задевает (ноль строк вне golden-конвейера/docs). Из
`docs/reviews/CODE-REVIEW-86-r3.md` (SHA `96bd3e3e`) принимается без
повторной аргументации:
- инвентарь 11 Party-1 ключей, отсутствие дублей title/`.rhint`, parity
RU/EN/DE/FR, source-контракт и мутационный гейт
`settings-help-party1-placement-removed` — код, который это доказывает
(`houseplan-editor-runtime.ts`, `houseplan-onboarding-runtime.ts`,
словари), в дельте `96bd3e3e..HEAD` не изменился ни на строку;
- cold-onboarding browser smoke (5 `space.*` подсказок) — файл
`demo/smoke_help_affordance.mjs` дельтой не тронут;
- AC7 «узкий viewport 390 px» (без zoom) — доказательство из r3
(`device-inbox-narrow-ru-dark`, `toggle-entity-dialog-mobile-ru`) не
затронуто, эти сценарии по-прежнему в матрице и `passed` в том же прогоне;
- AC8 (модель/поведение настроек не меняются) — `src/**` в дельте пуст, новых
рисков не появилось;
- находка **L1** (Low, снята без правки ещё в SPEC-REVIEW-86-r1 и
подтверждена в r1/r2/r3 code-review: `.help`-ключ не совпадает буквально с
ключом label) — дельта её не касается, решение прежних раундов остаётся в
силе, powторно не аргументирую.
## Как проверялось
Зелёный точный Validate уже есть на этом же SHA (`7408f8af`, run
`33309242147`, `headSha` совпадает побайтово, `conclusion: success`) — сверил
это сам через `gh run view --json headSha,conclusion`, а не со слов автора.
| Гейт | Как проверено | Результат |
|---|---|---|
| Typecheck, unit, build, sync 3 копий бандла, bundle:budget, no-new-any | job «Фронтенд» прогона `33309242147` | `success` — не перегонял сам (инструкция раунда: эти гейты на этом SHA уже подтверждены) |
| check-docs, provenance, process.yml sync, process-gate | job «Предполётные проверки», шаг «Вердикт»: `DOCS/WORKFLOW_SYNC/PROVENANCE/PROCESS_GATE = success` | все 4 зелёные |
| **Golden** (`npm run golden:verify`) | job «Golden-кадры против принятых эталонов» того же прогона: 147/147 `passed`, включая `settings-help-zoom-200-en-light` и `settings-help-zoom-200-ru-dark`; job «Переиспользование» подтверждает `Cache not found` для ключа golden — прогон настоящий, не переиспользован | `exitCode=0`, реальное исполнение на `7408f8af` |
| Смоки в браузере | job «Смоки» в этом прогоне — `skipped`, но легитимно: `gate-reuse.mjs` считает ключ smoke только по `demo/smoke_[^/]+\.mjs`, дельта их не трогает (`Cache hit`, шаг «Маркер smoke»); дополнительно прогнал `node scripts/smoke-select.mjs --base 96bd3e3e --head HEAD` лично — «Исполняемого frontend-диффа нет (src/**/*.ts не тронут). Browser-smoke этим диффом не выбираются» | пропуск обоснован инструментом, не тихий |
| Селекторы нового кода harness.mjs (`data-help-key`, `.trigger`, `.tooltip:popover-open`, `aria-expanded`, `_openSettingsDialog`) | сверил построчно с `src/hp-help.ts` (476–484) и `src/houseplan-card.ts`/`houseplan-editor-runtime.ts` (`_openSettingsDialog`) | все селекторы существуют в реальном коде, не выдумка |
| Два PNG, изменившихся в `docs/images/**` из-за сдвига общего `sourceFingerprint` | извлёк `06-device-editor.png` и `08-room-card.png` до/после дельты, сравнил пиксельно (`PIL.ImageChops.difference`) | разница — единицы пикселей на границах текста (суб-пиксельный anti-aliasing), визуально идентичны на глаз, содержимого не меняли; согласуется с тем, что дельта не трогает `src/**` |
| Трейлеры | `git show -s --format=%B` на `769cc13d`, `7408f8af` | `Issue: #86` на обоих; `User-Visible: no` на обоих (корректно — тест/тулинг, ни одного видимого пользователю изменения); `7408f8af` несёт `Release:`+`Baseline-Reviewed:` со ссылкой на зелёный Linux-прогон приёмки скриншотов |
| `test/golden-matrix.test.mjs` | прочитал новый тест целиком: проверяет ровно 2 сценария, оба темы, оба языка, `deviceScaleFactor: 2`, `viewport: 390×900`, `dialog: general-help`, `openHelp: gs.bg_mode.help`, `capture: page` | тест не тавтологичен для состава матрицы (упадёт, если сценарий переименуют/уберут атрибут) |
**Не прогонял сам и почему:**
- `npx tsc --noEmit`, `npm test`, `npm run build` + сверка копий бандла —
сняты условием раунда «дешёвые гейты на этом SHA уже подтверждены», сверил
только что Validate реально завершился на этом самом SHA;
- `npm run golden:verify` локально — не перегонял: `verify`-режим намеренно
не допускает `--scenario=`-фильтр (`policy.mjs: assertGoldenInvocation` —
«golden verify must run the complete matrix»), а полный прогон уже
подтверждён логом реального (не переиспользованного) исполнения на этом же
SHA; независимая проверка выполнена иначе — чтением assertion-кода и
визуальным осмотром принятых PNG;
- 12 «прямых» и 18 «слабых» смоков из `smoke-select.mjs` — инструмент сам
сообщил, что выбирать нечего (`src/**/*.ts` не тронут);
- `python -m pytest tests_backend -q` — diff не касается `custom_components/**/*.py`;
- мутационный гейт `settings-help-party1-placement-removed` — не перегонял:
мутирует шаблоны `houseplan-editor-runtime.ts`/`houseplan-onboarding-runtime.ts`,
дельта их не трогает, последний личный прогон — r1;
- `model-invariants`, `performance_smoke`, остальные ~193 браузерных смока —
diff не касается геометрии, ссылок на неё или perf-путей.
## Находки
Нет. High и Medium в скоупе не обнаружено. Единственная открытая находка —
унаследованная L1 (Low, снята без правки ещё в r1) — дельтой не задета,
повторно не поднимаю.
## Что проверено и корректно
- **AC7 (полностью, оба канала).** Смок (390 px + browser zoom 200%) закрыт
в r3 и не тронут этой дельтой; golden-канал теперь тоже закрыт —
представительный reviewed кадр `gs.bg_mode.help` в общих настройках, light
и dark, `deviceScaleFactor: 2`, семантическая проверка (`helpTextRegion`,
границы trigger/surface) — реально исполнен и прошёл на `7408f8af`, а не
заявлен на словах.
- **Golden-инфраструктура.** Рефакторинг `run.mjs` (пул страниц по
`deviceScaleFactor`, отдельный `pageErrors` на страницу) не сломал
остальные 145 сценариев — все `passed` в том же прогоне, включая уже
бывшие проблемными в r1 `device-inbox-*`/`toggle-entity-dialog-*`/`space-room-color-popover-desktop-ru`.
`assertFreshDemoBundle` по-прежнему вызывается на основной странице до
цикла сценариев — подмену бандла ловит как раньше.
- **AC1–AC6, AC8, AC9.** Ноль изменений в дельте вне golden-конвейера и
docs — унаследованы из r3 без пересчёта (см. «Унаследовано из r3»), риск
регрессии от этой дельты в них отсутствует по построению (дельта их файлов
не касается).
- **Одно число — один источник.** Дельта не добавляет и не меняет ни одной
пользовательской величины (только тестовый/docs-конвейер); правило
неприменимо.
- **Трейлеры и changelog.** `Issue: #86` на обоих коммитах дельты;
`User-Visible: no` корректно — ни один коммит не меняет видимое поведение
продукта, changelog не требовался и не трогался.
## Вывод
Находка r3 (M1) закрыта полностью и доказуемо: golden-кадр для AC7
«browser zoom 200%» существует, прошёл реальное (не переиспользованное)
исполнение на итоговом SHA, и я лично проверил его содержимое по трём
независимым линиям (assertion-код, пиксели PNG, CI-лог). Дельта с r3 не
трогает ни строки продуктового кода, поэтому остальные AC наследуются без
повторной проверки. High и Medium в скоупе — 0. Вердикт зелёный; по правилу
раунда бюджет циклов на зелёном вердикте не расходуется.