Files
houseplan-card/docs/reviews/SPEC-REVIEW-532-r1.md
T
2026-09-11 15:31:27 +00:00

167 lines
15 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.
# SPEC-REVIEW — issue #532 · заход r1
## Скоуп ревью
Issue #532, полный трек (владелец назвал нарушенный критерий §5: «нет влияния на
производительность» — задача целиком о нём). ТЗ живёт в теле issue под
заголовком `## ТЗ` (решение #517); ревью — этот документ, не комментарий, как и
положено полному треку. Раунд первый: `git log --oneline origin/dev..HEAD` для
#532 пуст, кода ещё нет — предмет ревью строго текст ТЗ и его согласованность с
текущим состоянием репозитория (`dev`@`116cfd5f`).
Материал: тело issue #532 (разделы до `## ТЗ` — симптом и замер S1; раздел
`## ТЗ` — собственно спецификация; `## Состояние при заведении` — метаданные
заведения) и два комментария («Аналитика (S2)», «ТЗ готово — на ревью (S3→S4)»).
Устных пояснений автора не запрашивалось.
## Как проверялось
1. Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` §2.3/2.4/7.1/7.2/§4,
`docs/SUN.md` (раздел «Current four-phase background»).
2. Каждое техническое утверждение ТЗ сверено с текущим кодом, а не принято на
слово:
- `grep` по `src/styles/plan.styles.ts` подтвердил дословно селектор К1 —
`.stage.daycycle .hp-paperg, .hp-static-stage.daycycle .hp-paperg` со
стопкой из трёх `drop-shadow` и `transition: filter 1100ms …` (строки
89–96), плюс дубль в `@media (prefers-reduced-motion: reduce)` (100–101).
Ровно то место, куда К1 просит добавить `will-change: filter`.
- `grep` по `src/space-render.ts`/`houseplan-card.ts` подтвердил, что класс
`daycycle` навешивается только когда `dayCycle` вычислен (т.е. только при
`bg_mode: 'daynight'`) — это подтверждает АC1/AC4 («при статичном фоне —
не несёт, и `filter` тоже отсутствует»): без класса `.daycycle` селектор
К1 не совпадает вовсе, значит ни `filter`, ни будущий `will-change` не
применятся.
- `demo/golden/matrix.mjs` подтвердил дословно цифры К2: сценарии
`day-cycle-{dawn,day,dusk,night}-dark` используют пресет `stage` —
`{ maxChannelDelta: 10, maxDiffRatio: 0.0005 }` (строка 6) — именно то,
что ТЗ называет действующим порогом.
- `src/glow-blend.ts` подтвердил утверждение К4: `svgScreenBlendSupported`
— рантайм-проба, не читающая `bg_mode`; довод «свет не зависит от фона»
обоснован кодом, а не только измерением.
3. Проверены на однозначность и способ доказательства все AC1–AC5 (таблица
ниже), включая то, умеет ли названный мутант/проверка отличить сломанную
защиту от рабочей — насколько это можно оценить до появления теста.
4. Сверены обязательные разделы §7.1 с текстом ТЗ (таблица ниже).
5. Проверено разделение «продуктовый вопрос / техническое решение» (§7.1,
владельцу — только о том, что видит и делает человек).
Гейты (`typecheck`/`test`/`build`) не запускались: на этой стадии нет кода
`#532` — `git log --oneline origin/dev..HEAD` для ветки задачи пуст, дерево
ровно на `116cfd5f`. Прогонять их нечего — это забота код-ревью следующего
этапа.
## Проверка обязательных разделов ТЗ (PROCESS.md §7.1)
| Раздел §7.1 | Есть в ТЗ | Где |
|---|---|---|
| Сценарий (персона/поверхность/момент) | да | «Продуктовая рамка»: домочадцы и админ в View, настенный планшет/телефон/десктоп, сам план |
| Что человек увидит до/после | да | «До.»/«После.» |
| Проблема | да (фактически) | абзац «До.» плюс разделы до `## ТЗ` (Симптом, замер аналитики) — единый контекст issue, а не выдумка |
| Скоуп и не-скоуп | да | К1 задаёт единственную правку; «Чего задача не трогает» — явный не-скоуп |
| Контракт поведения | да | К1–К4 |
| UX | да (в виде «нет») | «Ни одной новой настройки, ни одного нового элемента интерфейса» |
| Модель данных и миграция | да (в виде «нет») | «не трогает: … bg_mode и его миграции» |
| i18n | да (в виде «нет») | «не трогает: … i18n» |
| AC1…ACn с доказательством | да | таблица «AC и доказательства», у каждого — «чем доказан»/«чем краснеет» |
| План автотестов | да | новый смок `demo/smoke_daycycle_raster.mjs`, его регистрация в `smoke-links.mjs`/`check-inputs.mjs`, мутант в `mutation-gate.mjs` — раздел «Затронутые файлы» |
| Риски | да | 4 риска, включая главный технический (бюджет `will-change` в Gecko) с планом на случай провала |
| Откат | да | одна строка — ревёрт декларации |
| Release-артефакты | да | оба changelog, `docs/SUN.md`, `golden:verify` (AC3) |
Все обязательные разделы присутствуют по содержанию. Формально «Продуктовая
рамка» идёт не первым текстом под `## ТЗ` (перед ней — строка автора, раздел
«Документация» и «Чего задача не трогает») — §7.1 хочет продуктовые разделы
первыми. Содержательно это ни на что не влияет: у issue нет предыстории,
которую скрывают, здесь просто иной порядок изложения. Low, не блокирует, не
записываю отдельной находкой.
## Проверка AC на однозначность и доказуемость
| AC | Однозначен? | Доказательство названо? | Комментарий |
|---|---|---|---|
| AC1 | да — `getComputedStyle` даёт `will-change: filter` да/нет, `filter` присутствует/отсутствует, по двум значениям `bg_mode` | да, `demo/smoke_daycycle_raster.mjs`, часть 1 | падает от мутанта `daycycle-outline-not-promoted» — правдоподобно: он снимает декларацию, `getComputedStyle` перестаёт видеть `will-change` |
| AC2 | да — числовой порог «не больше чем вдвое», отношение, а не абсолютные мс | да, тот же смок, часть 2, CDP-трасса `RasterTask` | заявленный запас (0.26 против порога 2.0, и 7.9 без правки) восьмикратный в обе стороны — порог не хрупкий |
| AC3 | да — 4 конкретных golden id | да, `npm run golden:verify` | пороги сверены с `matrix.mjs`, совпадают |
| AC4 | да, но по факту вторая половина AC1 | да | не дефект, просто разнесено на два номера ради «краснеет» на другой мутации (декларация без `.daycycle`) |
| AC5 | да — два конкретных факта (текст в `SUN.md`, обе строки changelog) | да, «ревью кода построчно» — допустимый способ доказательства для документных AC | — |
Ни один AC не сформулирован как решение-без-обоснования: пороги (2.0; `7`/255
канала; `2%` шум между прогонами) — измеренные величины из комментария S2, а
не придуманные числа, и это видно по совпадению цифр между комментарием и ТЗ.
## Проверка «догадка вместо решения»
Отдельный технический раздел «Принято предположительно» правильно
отделяет то, что не наблюдает пользователь и что решает автор (`will-change:
filter` vs `transform`, порог 2.0, движок свидетеля — Chromium, метод замера —
CDP `RasterTask`), от продуктовых утверждений. Ни одно продуктовое
утверждение (вид не меняется, новых настроек нет) не подано как факт без
основания — оба подтверждены измерением (максимальное отклонение канала 7 при
пороге 10) либо тривиальны (правка — одна декларация, интерфейс не трогается).
Главная техническая неопределённость — бюджет памяти `will-change` в Gecko
может молча отклонить подсказку — не скрыта и не выдана за решённый вопрос:
она вынесена в Риск №1 с явным планом «если так — задача не закрыта,
переходит в К3». Это не относится к продуктовым вопросам владельца (§7.1:
только «что видит/делает человек» и «объём видимых изменений») — это
инженерная неопределённость стороннего движка, которую нельзя разрешить
вопросом, только измерением после реализации. Правильно, что автор сам назвал
её ревьюеру третьим пунктом «где спорить», а не спрятал.
## Находки
Нет ни одной находки High или Medium. Формальная асимметрия в порядке
разделов (см. выше) — Low, не блокирует и не создаёт риска для приёмки; в
исправлении не нуждается.
## Что проверено и корректно
- Все селекторы, свойства и значения, на которые ссылается ТЗ
(`.hp-paperg`, класс `daycycle`, стопка `drop-shadow`, `transition`, golden
id и их пороги, источник `screenBlend`), дословно совпадают с текущим
кодом на `116cfd5f` — ТЗ не описывает воображаемую версию файла.
- К3/К4 (слои окружения и блендинг света вне скоупа) обоснованы цифрами из
S2-аналитики (13% и «в пределах шума» соответственно), а не решением
«на глаз».
- AC2 использует отношение, а не абсолютные миллисекунды — устойчиво к шуму
раннера, восьмикратный запас в обе стороны от порога.
- Откат — одна строка, соответствует размеру правки.
- Не-скоуп корректно исключает миграции, i18n, touch-контракт, PDF-сцену —
все верно, ни один из них декларация `will-change: filter` не затрагивает.
## Чего не проверял
- Гейты `typecheck`/`test`/`build`/`golden`/смоки — нет кода для проверки на
этой стадии, дерево `#532` пусто относительно `dev`.
- Реальное поведение в Gecko/Firefox — по постановке самой задачи, это не
предмет автоматических AC, а предмет профиля владельца после реализации
(«Приёмка по скорости — отдельно»); дать вердикт по нему на этапе спецификации
нельзя и не нужно.
- Существование/содержимое ещё не написанных файлов
(`demo/smoke_daycycle_raster.mjs`, правки `mutation-gate.mjs`,
`smoke-links.mjs`, `check-inputs.mjs`) — это работа код-ревью следующего
этапа, когда файлы появятся.
## Вердикт
Зелёный. ТЗ полное по §7.1, каждый AC однозначен и снабжён способом
доказательства и способом падения, технические утверждения проверены по
текущему коду и не разошлись с ним, продуктовая рамка и не-скоуп на месте,
единственная значимая техническая неопределённость (реакция Gecko на
`will-change`) явно признана риском с планом на случай провала, а не выдана
за решённую. Готово к разработке.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `dev`, коммит `116cfd5fefc4` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `45453f44ff5e3243d824ab00132e42c14c9f7c33`
```
git log --all --format='%H %T' | grep 45453f44ff5e
```
- Тело issue: `b15dfcb6b8488979f9d1b9d85cad33d752f45ce246f228439ebf47e35f7e048a`
- Вердикт конвейера: `green` · High 0