docs: review document for #746

Issue: #746
User-Visible: no
This commit is contained in:
claude[bot]
2026-10-01 14:55:10 +00:00
parent 953f15b967
commit 255677556f
+175
View File
@@ -0,0 +1,175 @@
# CODE-REVIEW-746-r1
Issue: #746 · трек: show · заход: r1 · блокирующих циклов израсходовано 0 из 2
Материал: `aa0b60e75498d052625886d1cdc23190566f9bde` (ветка `issue/746-room-fill-alpha`,
один коммит поверх `dev` `653d94ef`, ребейз-без-изменения-кода зафиксирован автором
в комментарии issue от 2026-10-01T14:27:02Z)
Validate на этом SHA: https://github.com/Matysh/houseplan-card/actions/runs/36876972490 — success
## Скоуп
Баг: `.room { transition: 0.12s }` анимирует цвет и `fill-opacity`/`stroke-opacity`
независимо, видимая непрозрачность — их произведение. Состояния без заливки
(`overlay`, `yard`, `outlined`, `picked`) держали альфу **в цвете** (`rgba(...)`,
`*-opacity` по умолчанию `1`), а `.styled` — наоборот, непрозрачный цвет плюс
`fill-opacity: var(--room-fill-op)`. При переходе между половинами одна растёт,
другая падает, и продукт в середине пути больше обоих концов: комнаты вспыхивали
серым при открытии «Настроек пространства» на этаже без заливки и темнели при
входе/выходе из редактора плана на закрашенном этаже.
Правка приводит четыре состояния `.room` (`overlay`, `yard`, `outlined`, `picked`)
к той же записи, что уже была у `.styled`: непрозрачный цвет + явная
`fill-opacity`/`stroke-opacity`, `transparent` — только вместе с нулевой
`*-opacity`. Сам переход, `.styled` и переменные `--room-*` не тронуты.
Поверхность одна — блок правил `.room` в `src/styles/plan.styles.ts`;
`houseplan-space-card` берёт те же стили реэкспортом (`src/styles.ts`), отдельной
правки не требует — проверено чтением: `src/styles.ts:10,19` подключает
`planStyles` без переопределения `.room.*`.
Диапазон `git diff origin/dev...HEAD`: `src/styles/plan.styles.ts` (+24/-4),
новый `demo/smoke_room_fill_transitions.mjs`, `docs/CHANGELOG.md`,
`docs/CHANGELOG.ru.md`, `docs/STYLING-HOOKS.md` — один коммит, трейлеры
`Issue: #746` и `User-Visible: yes` на месте, оба changelog в том же коммите.
## Критерии §5 (трек show) — все пройдены
- **complexity** — issue оценивает сложность 2/10; правка — точечная замена
записи прозрачности в 4 блоках CSS-правил, без новой логики.
- **surfaces** — одна поверхность: правила `.room` в одном файле стилей,
переиспользуемые всеми рендерами плана.
- **migration** — нет миграции конфига; риск для пользовательских правил
card-mod (правило, задающее только `fill` без `fill-opacity`, станет
невидимым) явно описан в issue, в обоих CHANGELOG и в STYLING-HOOKS.md —
не обойдён молчанием.
- **ux-contract** — видимое поведение в покое не меняется (доказано пофреймово,
см. ниже); исправляется переходный артефакт, а не контракт.
- **perf-touch** — чистый CSS, без touch-поверхности; perf-смок в Validate
зелёный (не требовался AC, но прогнался сам).
- **undocumented** — ожидаемое поведение зафиксировано самим issue (таблица
AC с точными числами) и теперь — в `docs/STYLING-HOOKS.md` §3.2 (новый
абзац, ссылка на §3.3 «Everything generated» корректна).
Маршрут: `fix` (вердикт зелёный, `reclassify` неприменим).
## Как проверялось
| Гейт | Статус | Как |
|---|---|---|
| `tsc --noEmit`, `npm test`, `npm run build` + сверка бандла | не перегонял | зелёный Validate на точном SHA `aa0b60e7` (run 36876972490), "Фронтенд: типы, юниты, мутанты, синхрон бандла" — success |
| `golden:verify` | не перегонял сам, приняты результаты CI | тот же Validate, job "Golden-кадры против принятых эталонов" — success (`ci:golden` выставлена и отработала) |
| Смоки (полная матрица, 3 шарда) | не перегонял всю матрицу | тот же Validate, все три шарда "Смоки в браузере" — success; лог шарда 1 содержит `ok smoke_room_fill_transitions` — сам смок задачи выполнялся в CI и прошёл |
| `demo/smoke_room_fill_transitions.mjs` | **прогнал сам, дважды** | см. ниже — независимая проверка автотеста на red/green |
| `scripts/smoke-select.mjs --base origin/dev --head HEAD` | прогнал |「НЕОПРЕДЕЛЁННОСТЬ» — диффа в `src/styles/plan.styles.ts` символьная связь с смоками не выводится автоматически (CSS-правила, не функции); решение по AC1 issue и ручной проверке ниже |
| pytest / Hassfest / HACS / мутанты по диффу / геометрия TS-Python | не прогонял | нет правок Python/конфига/геометрии — неприменимо, в Validate эти джобы `skipped` |
| Мутанты (реестр) | не прогонял и не обязан | трек show, §10.4: мутанты на разработке не гоняются ни на одном треке |
**Чего не проверял:** полную матрицу golden и смоков заново (доверяю
зелёному Validate на точном материале — свежесть подтверждена #343); ручное
тестирование в браузере сверх смока (нет штатного гейта, Chromium не
запускал для этого — только для перепрогона смока, который сам headless);
поведение `.radarsetup .room` — явно вне скоупа в issue («Не-скоуп»).
### Независимая проверка AC1/AC2 (смок умеет падать)
Защитный AC — здесь это обязательная таблица «чем доказан · чем краснеет»:
| AC | Чем доказан | Чем краснеет |
|---|---|---|
| AC1: видимая непрозрачность заливки/обводки ни в одной точке перехода не превышает больший из концов более чем на 0.005 | `demo/smoke_room_fill_transitions.mjs`, прогнан мной на `aa0b60e7`: **OK**, все проверки (`fillOvershoot`/`strokeOvershoot` ≤ tolerance) зелёные для 4 путей + контроль | Я откатил патч `src/styles/plan.styles.ts` до состояния `origin/dev` (`git apply -R`), пересобрал бандл и перепрогнал тот же смок: **FAILED (6)**, с точными числами из issue — `openSettings` fill/stroke 0 → **0.2413** → 0, `cancelSettings` fill 0.18 → **0.3049** → 0 и stroke 0 → **0.2418** → 0, `enterPlanEditor` fill 0.18 → **0.3172** → 0.06, `leavePlanEditor` fill 0.06 → **0.314** → 0.18. Совпадает с цифрами issue (0.241/0.305/0.317/0.314) и отчётом автора. Тест действительно умеет падать именно на дефекте из issue, не на постороннем |
| AC2: кадр в покое не меняется (`overlay`, `styled`, `outlined`, `picked`, `yard`) | Тот же смок, секция `rest`: цвет и видимая непрозрачность по пяти состояниям совпадают с таблицей `REST`, зашитой в смок как ожидание дев-поведения; плюс Golden CI на точном SHA — success | На отклонение кадра покоя красит сам смок (`check` по каждому состоянию и каналу) — отдельно не провоцировал, т.к. правка кода не трогает значения `styled`/рест-часть `overlay` и т.д.; разбор по коду подтверждает: изменились только порядок/форма записи (hex→rgb + отдельная opacity), числовое произведение то же (проверено чтением, не исполнением, для каждой из 4 строк диффа — см. ниже) |
После обеих проверок рабочая копия и `dist/` возвращены ровно к материалу
ревью (`git checkout -- dist/`, `git clean -fd dist/`, `git status` чист,
`git rev-parse HEAD` = `aa0b60e75498d052625886d1cdc23190566f9bde`).
### Разбор по коду (проверено чтением, не исполнением)
Для каждого изменённого блока: произведение «альфа цвета × *-opacity» в покое
не меняется, а при переходе оба сомножителя теперь монотонны от 0 (или от
другого конца) к цели, поэтому продукт двух неубывающих/невозрастающих
кривых ограничен большим из концов — перелёта быть не может:
- `.room.overlay`: было `fill: transparent` (альфа 0, `fill-opacity` по
умолчанию 1) → стало `fill: transparent; fill-opacity: 0` — продукт 0 в
обоих случаях, при переходе к `.styled` оба множителя (альфа цвета и
opacity) растут от 0 монотонно → продукт ограничен произведением конечных
значений, перелёта нет. Симметрично для `stroke`.
- `.room.yard`: было `fill: rgba(75, 140, 90, 0.14)` (продукт 0.14) → стало
`fill: rgb(75, 140, 90); fill-opacity: 0.14` (продукт 0.14) — рест не
меняется, подтверждено прогоном (`rest.yard.fill = ["rgb(75, 140, 90)",
0.14]`).
- `.room.outlined`: `rgba(62,166,255,0.06)`/`0.55` → `rgb(...); fill-opacity:
0.06` / `stroke-opacity: 0.55` — рест не меняется (подтверждено прогоном).
- `.room.picked`: `rgba(255,193,77,0.25)` → `rgb(...); fill-opacity: 0.25`;
`stroke: #ffc14d` (уже непрозрачный) получил явный `stroke-opacity: 1` —
без визуального эффекта в покое, что и наблюдается (`rest.picked.stroke =
["rgb(255, 193, 77)", 1]`).
Побочный эффект, который автор сам назвал в отчёте и я подтвердил чтением:
у `.room.overlay:hover`/`.room.yard:hover` (наведение на незакрашенную
комнату) теперь обе половины (`stroke` и `stroke-opacity`) идут от 0 к 1
вместо одной — начало затухания визуально чуть мягче, монотонность
сохраняется, перелёта нет. Не находка: раскрыто в отчёте автора, не меняет
контракт, не заявлено как AC.
### Проверка «одно число — один источник»
Числа 0.14 / 0.06 / 0.55 / 0.25 (видимые альфы `yard`/`outlined`/`picked`)
встречаются только в `src/styles/plan.styles.ts` — их единственный источник.
Поиском (`grep` по RGB-триплетам `75,140,90` / `62,166,255` / `255,193,77`
вне `plan.styles.ts`) второго места с тем же числом не нашлось; совпадение в
`src/styles/devices.styles.ts:379` (`rgba(62, 166, 255, 0.22)`) — другой
компонент (устройство), другое число (0.22), не дублирует величину задачи.
`demo/smoke_merge_highlight.mjs` сверяет только подстроку цвета
(`includes('255, 193, 77')`), а не альфу — совместимость с переходом на
`rgb(...)` + отдельный `fill-opacity` подтверждена прогоном CI (шард 1:
смок зелёный).
## Что проверено и корректно
- Диапазон диффа `origin/dev...HEAD` — ровно заявленное в ТЗ: `.room.overlay`,
`.yard`, `.outlined`, `.picked`; `.styled` и переменные `--room-*` не
тронуты (построчная сверка).
- `houseplan-space-card` подтверждённо берёт те же стили без дублирования
правил.
- Трейлеры `Issue: #746`, `User-Visible: yes` на коммите; оба CHANGELOG
правлены в том же коммите, текст совпадает с AC3 issue дословно (RU и EN).
- `docs/STYLING-HOOKS.md` получил абзац, фиксирующий новый инвариант как
контракт — ссылка на §3.3 «Everything generated» для самих величин
корректна (значения остаются негарантированными, инвариант записи —
гарантирован).
- Смок умеет падать именно на дефекте issue (см. раздел выше) — не ложно
положительный.
- CI на точном SHA материала: Validate, Golden, вся матрица смоков (включая
новый) — зелёные.
- Метки issue соответствуют треку и объёму: `track:show`, `ci:golden`,
`S7-code-review`.
- Риски (невидимость для правил card-mod без явной `*-opacity`, более мягкое
начало hover-замирания) названы автором и подтверждены разбором, задокументированы
в CHANGELOG/STYLING-HOOKS — не висящие Medium.
## Находки
Нет. High: 0, Medium: 0 (ни в скоупе, ни вне скоупа).
## Вердикт
Зелёный. AC1 и AC2 доказаны автотестом, тест проверен на способность
падать лично (red на отменённом патче с теми же числами, что в issue),
AC3 (гейт/CHANGELOG) выполнен. Побочных дефектов не найдено, риски раскрыты
и задокументированы.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/746-room-fill-alpha`, коммит `aa0b60e75498` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `0d71ae29c68313fe4be018c5026ec20ccb600cc8`
```
git log --all --format='%H %T' | grep 0d71ae29c683
```
- Тело issue: `e2d6161bb012959bb539ad49b1ed92bdc86fdc4cb703235caab7bca18258dc38`
- Вердикт конвейера: `green` · High 0 · маршрут `fix`
<!-- hp:usage input_tokens=4495 output_tokens=23527 cache_creation_input_tokens=86008 cache_read_input_tokens=3392622 num_turns=56 -->