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

15 KiB
Raw Blame History

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), что автоматический прогон упал и статусная метка не переставилась. С точки зрения оркестратора раунд не завершён (бюджет §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 не требуется.