docs: review document for #266

Issue: #266
User-Visible: no
This commit is contained in:
claude[bot]
2026-08-25 22:44:15 +00:00
parent 3a49b14274
commit 0d62a48b1e
+171
View File
@@ -0,0 +1,171 @@
# SPEC-REVIEW-266-r3
Issue: [#266](https://github.com/Matysh/houseplan-card/issues/266) — «Рефакторинг 3/5: расщепить styles.ts»
ТЗ: `docs/specs/266-split-styles.md` (коммит `3a49b142`, ветка `issue/266-split-styles`)
Предыдущий раунд: SPEC-REVIEW-266-r2, вердикт жёлтый (Medium М2), ТЗ проверялось на коммите `afb6e151`
Этап: spec (PROCESS.md §2.4) · заход r3 · блокирующих циклов израсходовано 2/4
Вердикт: **зелёный**
## Скоуп ревью
Третий заход, разбор по дельте (PROCESS.md §2.10, issue #214). Предмет — изменения
ТЗ между `afb6e151` (на чём получен вердикт r2) и `3a49b142` (текущий HEAD):
```
git diff afb6e151..3a49b142 -- docs/specs/266-split-styles.md
```
Дельта — 4 строки: строка «Статус», AC2 (арифметика прототипа), AC6a (счёт
`prefers-reduced-motion`-блоков 8→10). Правка только текстовая, точечная,
адресует ровно находку M2 из r2 — не ребейз, не смена контракта поведения, не
новая подсистема, объём несопоставим с исходной задачей → полный повторный
разбор не требуется. Заново проверены AC2 и AC6a и всё, до чего дотягивается
эта дельта (числа, на которые они ссылаются, и юнит, который их фиксирует).
Остальной текст ТЗ наследуется из r2 без повторной проверки (раздел ниже).
**Важное отступление от обычного заходa этой стадии.** Между r2 и r3 в ветку
`issue/266-split-styles` дополнительно попали 6 коммитов реализации
(`cbdc2489`, `c684f178`, `7d094fd2`, `bf52524d`, `55f7126a`, `0d9320fe`) —
инструмент-генератор слайсов и все пять файлов `src/styles/*.styles.ts` плюс
финальный сборщик. Автор сообщил об этом честно в комментарии к ревизии 3:
подготовленные заранее коммиты реализации уехали в публикуемую ветку по
ошибке (несработавший `stash` перед `push`, унесённый ребейзом), историю
опубликованной ветки не переписывает. Разбор ниже — отдельным разделом
«Процессное замечание», не как находка по тексту ТЗ.
## Как проверялось
- Прочитан диф `afb6e151..3a49b142` целиком для `docs/specs/266-split-styles.md`
(выше) и полный текущий текст файла.
- Прочитан документ `docs/reviews/SPEC-REVIEW-266-r2.md` (коммит `34335d1e`) —
находка M2 и её формулировка.
- Арифметика AC2 пересчитана вручную: `248+381+540+1180+1359+19 = 3727`;
порог `3690×1.05 = 3874.5`; `3727 ≤ 3874.5` — сходится, разрыв r2 устранён.
- Поскольку реализация уже физически лежит в ветке (см. отступление выше),
числа AC2 и AC6a сверены не только с текстом ТЗ, но и с фактическим кодом —
строгая проверка, а не совпадение на слово автора:
- `wc -l src/styles.ts src/styles/*.styles.ts` → `19 / 248 / 381 / 540 /
1180 / 1359` — совпадает с AC2 буквально, каждый файл под лимитом 1650,
сумма 3727 под порогом 3874;
- `grep -c '@media (prefers-reduced-motion: reduce)'` по пяти новым файлам
→ 10 совпадений (`base.styles.ts`:1, `chrome.styles.ts`:1,
`devices.styles.ts`:2, `plan.styles.ts`:6); `grep` того же паттерна по
`src/styles.ts` на коммите r2 (`afb6e151`, до переноса) → тоже 10 — AC6a
«10» верен, старые «8» из r1/r2 были ошибкой счёта, как и признаёт текст;
- `@media (forced-colors: active)` — 2 блока и до, и после (в
`plan.styles.ts`, строки 314 и 1189) — совпадает с AC6a;
- прочитан `test/styles-split.test.mjs` — юнит буквально реализует то, что
AC4/AC5/AC6a описывают (порядок сборщика, непересечение селекторов со
списком исключений, счёт медиа-обёрток 2/10), и способен упасть: любое из
трёх чисел, порядок массива или лишний общий селектор ломает assert.
- Проверено наличие `demo/smoke_plan_snap_overlay.mjs` и `demo/smoke_preloader.mjs`,
названных в §7 ТЗ как обязательные на слайсах `plan`/`base` — оба файла на
месте.
- process-gate прогнан для диагностики процессного замечания (см. ниже):
`node scripts/process-gate.mjs --issues` → `FAIL п.8 статус issue — issue
#266: статус S4-spec-review, а нужен один из S5-ready / S6-in-progress /
S7-code-review / S8-merged`.
## Закрытие раунда r2
| Находка r2 | Чем закрыта | Где видно |
|---|---|---|
| **M2** (Medium, в скоупе) — AC2 «факт» 4431 строка не проходил собственный порог 3874.5, разрыв не объяснён | AC2 переписан: источник роста назван явно (пустые строки-разделители генератора-прototipa), приведён уплотнённый замер 248/381/540/1180/1359+19=3727 ≤ 3874 | `docs/specs/266-split-styles.md`, AC2 (диф `afb6e151..3a49b142`); арифметика и цифры перепроверены против фактических файлов `src/styles/*.styles.ts` (раздел «Как проверялось») — совпадают буквально |
Побочно, вместе с M2, автор поправил и AC6a (счёт `prefers-reduced-motion` 8→10) —
это не было находкой r2 (r2 унаследовал L2-риск как «не критично»), но правка
корректна и подтверждена: фактический счёт в источнике на момент r2 (`afb6e151`)
и сейчас — 10, «8» было ошибкой обоих предыдущих раундов, как и пишет автор.
## Унаследовано из r2
Без повторной проверки, документ `docs/reviews/SPEC-REVIEW-266-r2.md` на `afb6e151`:
- §0 (сценарий), §1.1 (структура каталога, распределение по кластерам),
§1.2 (сборщик, внешний контракт `cardStyles`) — не тронуты дельтой r2→r3;
- §1.3.1–§1.3.4 (golden как главный критерий, запрет дубликатов, порядок
склейки, бюджет бандла) — не тронуты;
- §1.3.5/§1.3.6 (scope-ключ сверки, обязательные смоки `forced-colors`/
`reduced-motion`) — унаследованы из r2, который сам унаследовал их как
закрытие M1 из r1 (`SPEC-REVIEW-266-r1.md` на `4c93f28e`);
- §1.4 (порядок слайсов), §2 (скоуп/не-скоуп), §3 (UX/i18n/touch), §4 (риски),
§5 (release-артефакты), §8 (откат) — не тронуты;
- AC1, AC3, AC4, AC5, AC6, AC7, AC8 — текст не менялся дельтой r2→r3;
доказательства AC4/AC5/AC6a перепроверены заново в этом раунде (см. выше),
поскольку дельта их задевала через AC6a; AC1/AC3/AC6/AC7/AC8 наследуются
без повторной проверки;
- классификация «класс A, полный флоу», обязательные разделы §7.1 — приняты
в r1, не пересматривались.
## Процессное замечание (не находка по ТЗ, не блокирует вердикт)
Между r2 и r3 в ветку попали 6 коммитов реализации класса A (`src/**`):
инструмент `scripts/dev/styles-split.mjs` и пять файлов
`src/styles/*.styles.ts` + финальный сборщик `src/styles.ts` — то есть
продуктовый код написан и закоммичен, пока issue #266 находится в статусе
`S4-spec-review`, раньше «Готово к разработке». Это прямое совпадение с
формулировкой PROCESS.md §12: «Запрещено: код без issue или из статуса
раньше «Готово к разработке»».
Автор сообщил об этом сам и честно (комментарий к ревизии 3): заранее
подготовленные коммиты реализации уехали в публикуемую ветку по ошибке
(несработавший `stash` перед `push`, унесённый последующим ребейзом), история
опубликованной ветки не переписана. Намеренного нарушения или сокрытия нет.
Тем не менее это стоит зафиксировать отдельно, а не просто отметить в тексте
ревью (§12: «оставили в тексте ревью» закрытием не считается — этот принцип
для находок out-of-scope, но по духу применим и здесь, раз правило нарушено
буквально). Дополнительно: механическая проверка это правило действительно
умеет ловить (`node scripts/process-gate.mjs --issues` → `FAIL п.8`), но
только пока метка issue равна текущей `S4-spec-review`. Как только этот
вердикт станет зелёным и метка перейдёт в `S5-ready` (штатный следующий шаг),
повторный прогон того же гейта на тех же коммитах пройдёт зелёным — проверка
п.8 сверяет **текущую** метку issue с диапазоном коммитов, а не метку на
момент, когда коммит был написан. То есть нарушение, которое сейчас поймано
случайно (ревью совпало по времени с окном, где метка ещё не сдвинулась),
станет невидимым естественным ходом процесса — ровно случай из PROCESS.md
§12: «если правило удалось нарушить незаметно, виновата проверка».
Завёл отдельный issue с меткой `process` (не блокирует эту задачу и не входит
в бюджет циклов #266): **#311** «process-gate п.8 проверяет текущую метку
issue, а не метку на момент коммита — код #266 обгонит собственную DoR
незаметно».
Это не находка по содержанию ТЗ — сам документ `docs/specs/266-split-styles.md`
не пишет и не подтверждает несуществующее поведение задним числом; наоборот,
случайно попавшая реализация дала возможность сверить AC2/AC6a не с оценкой
автора, а с фактическим кодом (см. «Как проверялось»), что только укрепило
уверенность в ревизии 3. Вердикт по ТЗ не меняется этим замечанием.
## Что проверено и корректно
- Оба обязательных первых раздела (§0 «Сценарий», «Что человек увидит») на
месте и не тронуты дельтой — задача refactor-only, продуктового поведения
нет, сформулировано честно ещё в r1 и не оспаривалось.
- Все обязательные разделы §7.1 присутствуют (структура, скоуп/не-скоуп,
контракт, UX/данные/i18n/touch, AC1…AC8 с доказательством у каждого, план
автотестов, риски, откат, release-артефакты).
- Догадок, выданных за факт, не найдено: единственное новое утверждение
ревизии 3 (счёт reduced-motion-блоков «10», источник роста прототипа) явно
подтверждено способом получения (`grep -n` по исходнику) и совпадает с
фактическим кодом ветки.
- Арифметика AC2 сведена и корректна (пересчитано вручную и против кода).
- Открытых продуктовых вопросов нет.
## Чего не проверял
- Тяжёлые гейты (`typecheck`/`test`/`build`/`golden`/смоки/мутационный
гейт) прогонами не запускал. Дельта этого раунда — 4 строки текста ТЗ, для
которых это несоразмерно; сами гейты и полный код — предмет код-ревью
(`S7-code-review`), которое ещё не началось (issue не покидал S4).
Совпавшие с этим раундом коммиты реализации сверил точечно (`wc -l`, `grep`,
чтение `test/styles-split.test.mjs`) ровно в объёме, нужном для проверки
AC2/AC6a этого ТЗ, а не как код-ревью — полный аудит реализации (стиль,
покрытие мутационного гейта, побочные правки в `scripts/fix-test-build.mjs`
и `scripts/mutation-gate.mjs`) будет сделан на своём этапе.
- Не проверял, синхронизированы ли три копии бандла (`dist/`,
`custom_components/houseplan/frontend/`, `demo/srv/assets/`) — не предмет
спек-ревью.
- Не запускал `demo/smoke_plan_snap_overlay.mjs` / `demo/smoke_preloader.mjs`
сам — только подтвердил их существование; коммит-сообщения слайсов 4 и 5
заявляют «OK», перепроверка результата смоков — код-ревью.