Files
houseplan-card/docs/reviews/CODE-REVIEW-252-r2.md
T
2026-08-23 08:06:22 +00:00

177 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.
# CODE-REVIEW-252-r2
Вердикт: зелёный · заход r2 · блокирующих циклов израсходовано 0 из 4 · High: 0 · Medium: 0
## Скоуп раунда: почему это переиздание r2, а не новый разбор с нуля
Этот запуск — повторное проведение того же раунда r2, а не заход r3. Round r2
уже был проведён и опубликован (комментарий issue от 2026-08-23T07:56:33Z,
документ `docs/reviews/CODE-REVIEW-252-r2.md`, зелёный вердикт, High:0/Medium:0),
но пайплайн не довёл дело до конца: автор сообщил (2026-08-23T07:56:45Z), что
автоматический прогон [упал](https://github.com/Matysh/houseplan-card/actions/runs/32625878719)
и статусная метка не переставилась. С точки зрения оркестратора раунд не
завершён (бюджет §4 не потрачен: зелёный вердикт цикл не расходует), поэтому
задача пришла на повторное r2, а не на r3.
Проверено, что переиздавать нечего заново с нуля:
- SHA, на котором был получен предыдущий вердикт r2: `b58136aa2cc943652af5adb8a94047b668d68dc6`.
- Текущий HEAD: `3741bddc6236ffe3d85965dc630704e578561710`.
- `git diff b58136a..HEAD --stat` → ровно один файл:
`docs/reviews/CODE-REVIEW-252-r2.md | 249 +++++++++++++++++++++++++++++++++++++`
(1 file changed, 249 insertions(+)) — это САМ ранее опубликованный документ
ревью, закоммиченный шагом публикации предыдущего (упавшего после вердикта)
прогона. Ни один файл `src/**`, `demo/**`, `docs/CANVAS.md` и т.п. между
`b58136a` и текущим HEAD не менялся.
Значит, весь код, который уже был полностью разобран в CODE-REVIEW-252-r1
(на пре-ребейзном коммите, полный разбор) и CODE-REVIEW-252-r2 (на
пост-ребейзном `b58136a`, тоже полный разбор — ребейз на ушедший вперёд `dev`
подпадает под §7.2), остался байт-в-байт тем же кодом. Дельта этого раунда —
пустая по существу. Полный разбор AC по коду в третий раз подряд на неизменном
дереве был бы именно той «потерей времени», от которой явно предостерегает
инструкция («полные наборы — это предрелизный гейт, а не гейт ревью»).
Дополнительно проверено расхождение с `origin/dev`, который тем временем ушёл
дальше собственной прошлой проверки в CODE-REVIEW-252-r2 (там уже был учтён
`a952f5f`/#256 и его review-документ `2d1fca1`, оба признаны не пересекающимися
с #252 по файлам). С тех пор `dev` получил ещё два коммита:
- `10999a5` — `ci(process): привести ветку к dev до код-ревью, а не после` (#257),
правит только `.github/workflows/process.yml`;
- `4b6331f` — `docs(process): описать приведение ветки к dev до код-ревью` (#257),
правит только `PROCESS.md`.
Оба — чистый процесс/CI, ноль пересечения по файлам с диффом #252
(`src/space-reference-repair.ts`, `plan-optimizer.ts`, `houseplan-card.ts`,
`styles.ts`, i18n, доки CANVAS/CONFIG-COMPATIBILITY/USER-GUIDE, golden-сцены).
Ретроактивно новое правило «ребейзить до ревью» на уже идущий с r1 код-ревью
#252 не распространяется (правило описывает будущий шаг пайплайна перед
следующим запуском ревью, а не требование к уже проверенному коду). Само
слияние ветки #252 в `dev`, как и раньше, потребует технического ребейза —
это не меняет оценку текущего диффа.
## Закрытие раунда r1 (CODE-REVIEW-252-r1)
| Находка r1 | Чем закрыта | Где видно |
|---|---|---|
| Low, снято с записью на будущее: мёртвый ключ `gs.optimize_reference_warning` остался в `en.json`/`ru.json`, код его больше не вызывает | Ключ полностью удалён из обоих словарей коммитом `6c779b5` | `git diff origin/dev...HEAD -- src/i18n/en.json src/i18n/ru.json` — строка присутствует только как удаление; `test/i18n.test.mjs:77` (`assert.doesNotMatch(..., /this\._t\('gs\.optimize_reference_warning'/)`) прогнан лично в этом раунде в составе `npm test` (1140/1140) — зелёный |
Это закрытие не изменилось со времени предыдущего r2 — код тот же самый.
## Унаследовано из r1
Полный построчный разбор кода (`src/space-reference-repair.ts` целиком,
изменённые фрагменты `plan-optimizer.ts`/`houseplan-card.ts`/`styles.ts`,
все юнит- и smoke-тесты, канонические доки, release-артефакты) взят из
CODE-REVIEW-252-r1 (документ в `docs/reviews/CODE-REVIEW-252-r1.md`, SHA
проверки в этом раунде — `b58136aa2cc943652af5adb8a94047b668d68dc6`, где этот
разбор был повторён ПОЛНОСТЬЮ заново после ребейза, а не как наследование —
см. раздел «Скоуп» того документа). Наследуется без повторного построчного
чтения в этом раунде:
- классификация владельца (`absent`/`live-in-missing-space`/`unverified`) —
доказательная схема, три исхода, fail-closed при неполном/неавторитетном
registry и при неизвестном namespace (AC1–AC3);
- отчёт без внутренних id в основном тексте, id — только в свёрнутых
«Подробностях» (AC4);
- Preview/Cancel/Apply/Undo и идемпотентность #248 не регрессируют (AC5, AC6);
- осознанное и задокументированное расширение поведения detach у #244
(немедленное авто-удаление stale-позиции заменено общей классификацией;
переименованные тесты и `docs/CANVAS.md`/`docs/CONFIG-COMPATIBILITY.md`
прямо называют это заменой, а не регрессом);
- `docs/CANVAS.md:494-497` заменён (не дополнен), release-артефакты (оба
changelog, канонические доки, синхронные bundle-копии) в одном
поведенческом коммите с `User-Visible: yes`;
- golden-семантика двух #252-сцен (`optimize-orphan-references-dark-en`,
`optimize-orphan-references-light-ru`), доказанная полным
`golden:verify` (97/97) дважды — на пре-ребейзном дереве в r1 и на
пост-ребейзном `b58136a` в r2.
Основание доверять этому наследованию без повторного чтения — не слова
автора, а свежая проверка в этом раунде (см. ниже), что дерево с тех пор не
изменилось ни на байт.
## Как проверялось в этом раунде
Лично прогнано на текущем HEAD (`3741bdd`), не со слов автора и не по
памяти о прошлых раундах:
- `npx tsc --noEmit` → чисто, без вывода;
- `npm test` → 1140/1140 pass, 0 fail, 0 skipped (включает
`test/model-invariants.test.mjs` — 12/12, в т.ч. `#253: исчезнувшая запись
толщины` и `readModel понимает экспорт/config/get/сырой config (#254)` —
обязательный гейт, диф трогает layout-ссылки на пространства);
- `npm run build` → зелёный; `sha256sum dist/houseplan-card.js
custom_components/houseplan/frontend/houseplan-card.js` →
`e5389ba8e8250c6030fb5365b81619b0d2b4687b4c32f2f2c227ca527b7dbcec` для
обеих копий — **тот же хеш**, что зафиксирован в CODE-REVIEW-252-r1
независимо от меня в этом раунде; совпадение хеша — самостоятельное
машинное доказательство того, что исходный код не менялся с r1/r2, а не
доверие на слово;
- `npm run bundle:sync` → пересобрал и синхронизировал нетрекаемую
стенд-копию `demo/srv/assets/houseplan-card.js` (не коммитится с #255) —
зелёно;
- `node scripts/check-docs.mjs` → «Documentation checks passed (7 files, 10
external links)» — обязателен, диф трогает `src/**`;
- `node scripts/mutation-gate.mjs --check` (дешёвый режим, без пересборки
бандла на каждого мутанта) → 139/139 `ok`, включая все четыре мутанта
этой темы: `orphan-space-detach-disabled`,
`orphan-space-ambiguous-signature-guessed`,
`orphan-cleanup-partial-registry-deletes`,
`orphan-cleanup-proven-owners-kept`. Дорогой полный прогон (пересборка на
каждого мутанта) не повторялся: он уже дважды пройден целиком (r1 —
136/136, r2 — переподтверждён) на этом же дереве, а `--check` подтверждает,
что реестр патчей и код с тех пор не разошлись;
- `node scripts/smoke-select.mjs --base origin/dev --head HEAD` → те же 6
смоков, что в r1/r2: `smoke_orphan_space_references`, `smoke_grid_snap`,
`smoke_optimize_coordinate_canonicalization`,
`smoke_optimize_geometry_preflight`, `smoke_optimize_micro_interval`,
`smoke_warm_dialogs`; остальные 165 не пересекаются по символам
(инструмент не назвал других связей);
- все 6 отобранных смоков лично прогнаны headless-Chromium в этом раунде —
все `OK`, все булевы поля результата `true` (в т.ч.
`idsExistOnlyInClosedDetails`, `explicitCleanupRebuildsPreviewWithoutWriting`,
`undoRestoresDeadRefs` из `smoke_orphan_space_references`).
Не повторял в этом раунде (обоснование):
- **`golden:verify` (полный, 97 сценариев).** Уже пройден целиком дважды в
этом же код-ревью: в r1 на пре-ребейзном дереве и в r2 на `b58136a` —
оба раза 97/97, включая обе #252-сцены. Текущее дерево байт-в-байт
идентично `b58136a` (доказано выше диффом и совпадением SHA-256 бандла).
Третий полный прогон на неизменном дереве не добавил бы информации и
прямо противоречил бы правилу соразмерности гейтов ревью.
- **`mutation-gate.mjs` без `--check` (полная пересборка на мутанта).** По
той же причине — уже дважды 136+/136+ на этом дереве, `--check` в этом
раунде подтвердил отсутствие расхождения.
- **`python -m pytest tests_backend`** — диф не трогает
`custom_components/**/*.py` (только сгенерированный JS-бандл в той же
папке).
- **`npm run invariants -- --config <файл>`** — точечного экспорта живой
инсталляции для этой ветки по-прежнему нет; вместо него — `npm test`
прогнал `test/model-invariants.test.mjs` на всех fixture-моделях проекта
(см. выше), это тот же `checkReferences`, что стоит за флагом.
- **Performance-профили** — не названы в AC8, путь maintenance-only, не
render loop (§12 ТЗ).
- **Ручное браузерное тестирование** — не входит в конвейер ревью; заменено
headless smoke (свежепрогнанные) и дважды пройденным полным golden.
## Находки
Нет находок уровня High или Medium. Low-находка r1 (мёртвый i18n-ключ)
закрыта — см. таблицу выше.
## Итог
Код не изменился с зелёного вердикта предыдущего r2 (`b58136a`) ни на
байт — единственная разница в дереве это сам ранее опубликованный документ
ревью. Этот раунд — техническое переиздание того же вердикта после сбоя
пайплайна на шаге после публикации (перестановка метки), а не пересмотр
кода. Все дешёвые гейты, обязательные для каждого раунда, лично прогнаны
заново на текущем HEAD и зелёные, включая независимое совпадение SHA-256
собранного бандла с зафиксированным в r1. AC1–AC8 остаются подтверждёнными
полным разбором r1/r2 (см. «Унаследовано из r1» и исходные документы).
Дрейф `origin/dev` (#256, #257) не пересекается с диффом #252 и не требует
пересмотра. Новых issue не требуется.