Files
houseplan-card/legacy/reviews/v1.65.0/CODE-REVIEW-204-r1.md
T
Claudeandclaude[bot] 0991c45374 fix(tools): архив переписывает относительные ссылки перенесённых документов (#682)
Ревью #682 r1, Medium: перенос добавляет документу уровень вложенности
(`docs/reviews/X.md` → `legacy/reviews/<тег>/X.md`, `docs/specs/` →
`legacy/specs/`), а относительные ссылки внутри перенесённых документов и в
соседях, ссылавшихся на них, никто не пересчитывал — на `97d19268` 53 битые
ссылки в 46 файлах (заявление «все 26 резолвятся» в `7feb6177` было верно
только до переноса документов ревью). Гейты архив не смотрят.

`reviews-archive.mjs`: `repairLinks` пересчитывает ссылку, если она не
резолвится от нового места, а цель находится от нового или старого места
через карту переносов; битая и до переноса ссылка не трогается. `--apply`
делает это само, `--repair-links=<rev>` — для всех переименований
`<rev>..HEAD`, `--check-links` печатает битые. Этим коммитом
`--repair-links=origin/dev` переписал ровно 53 ссылки в 46 файлах; остались
две прежние «...»-заглушки в CODE-REVIEW-448-r2 (битые и на dev). Тесты:
перенесённый документ, сосед со ссылкой в архив, ТЗ со ссылкой на позже
перенесённое ревью, битая-до-переноса не трогается, в `legacy/` битых нет;
мутант `reviews-archive-links-from-new-place-only`. PROCESS §2.10 и
DEVELOPMENT › Release называют переписывание и `--check-links`.

Issue: #682
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-27 22:10:47 +00:00

14 KiB

CODE-REVIEW-204-r1

  • Issue: #204
  • ТЗ: docs/specs/204-space-create-display-defaults.md
  • Ветка: issue/204-space-create-display-defaults, коммит 12c4f77
  • Диапазон: origin/dev..HEAD = f703242 (spec) → ec0ccaf (spec review doc) → 12c4f77 (реализация)
  • Вердикт: зелёный

Скоуп ревью

Этап code по PROCESS.md §2.7: реализация против ТЗ #204 (единственный implementation-коммит 12c4f77, User-Visible: yes). Задача — честные create-dialog defaults для «Всегда отображать границы комнат» и «Отображать названия комнат»: File false/false, Draw true/true, сохранение выбора пользователя (displayTouched) при последующих File↔Draw, и убрать скрытую Save-подмену draw && mode==='create' ? true : d.showBorders. Обычный Create и Floors/Areas onboarding должны следовать одной политике.

Как проверялось

  1. Прочитаны в порядке: docs/SCOPE.md, AGENTS.md, PROCESS.md §2.7, тело issue #204 и все комментарии (аналитика, ТЗ, код-ревью спеки, реализация), docs/USER-GUIDE.ru.md (раздел «Пространства» — таблица «Источник плана» уже содержала термин «Без изображения» до этого коммита), docs/UX-MODES.md (подтверждено: описывает только рантайм-резолвер show_borders/show_names, не create-dialog UX — не затронут, non-scope ТЗ верен).
  2. Прочитан весь git diff origin/dev...HEAD построчно: новый модуль src/space-dialog.ts, точки интеграции в src/houseplan-card.ts (_openSpaceDialog('create'), _openNextImport, _saveSpaceDialog, радиокнопки File/Draw, оба _boolInput переключателя), новый unit-файл test/space-dialog.test.mjs, новый браузерный smoke demo/smoke_space_create_display_defaults.mjs, правка существующего demo/smoke_space_settings.mjs, запись мутанта в scripts/mutation-gate.mjs, документация (CHANGELOG.md/.ru.md, USER-GUIDE.ru.md, TESTING.md, STATUS.md, docs/specs/README.md), tsconfig.test.json.
  3. Построчно сверена корневая причина ТЗ (§3) с диффом: скрытая ветка show_borders: draw && d.mode === 'create' ? true : d.showBorders (и аналог для show_names) в _saveSpaceDialog() заменена на прямую запись d.showBorders/d.showNames — подмена действительно убрана, не переименована.
  4. Проверен весь конечный автомат displayTouched (§6 ТЗ) против src/space-dialog.ts: initialSpaceDisplayDraft (File false/false, displayTouched:false), switchSpacePlanSource (проецирует source-default пока !displayTouched, иначе меняет только source), touchSpaceDisplay (ставит displayTouched:true, второй control не трогает) — все 6 пунктов контракта покрыты один в один, скрытых переходов не осталось.
  5. Проверено, что обычный Create (_openSpaceDialog('create'), src/houseplan-card.ts:13239) и каждый шаг onboarding (_openNextImport, src/houseplan-card.ts:13613) используют один и тот же ...initialSpaceDisplayDraft() — общий чистый state, без риска утечки touched между этажами (AC5).
  6. Проверено, что Edit (src/houseplan-card.ts:13214) выставляет displayTouched: true сразу — источник переключается без авто-сброса уже сохранённых values (AC6), что и есть корректное поведение «Edit не должен применять create-only defaults».
  7. Прогнаны все заявленные автором гейты локально (см. таблицу ниже), включая исполнение (не чтение) целевого мутанта — тест обязан был покраснеть без патча и покраснел на мутанте.
  8. Проверены трейлеры всех трёх коммитов диапазона (Issue: #204 на каждом; User-Visible: no на двух docs-коммитах, User-Visible: yes на реализации) и что оба changelog (docs/CHANGELOG.md, docs/CHANGELOG.ru.md) правлены в том же коммите 12c4f77, что и продукт (git show --stat 12c4f77).
  9. Проверено побайтовое совпадение трёх копий бандла после чистой пересборки (dist, custom_components/houseplan/frontend, demo/srv/assets) — идентичны и git diff после пересборки пуст, т.е. закоммиченные копии не разошлись с исходником.

Гейты — что прогнано и результат

Гейт Статус Результат
npx tsc --noEmit прогнан green, без ошибок
npm test прогнан 916/916 green (включая 4 новых теста space-dialog.test.mjs)
npm run build + сверка 3 копий бандла прогнан build green; md5sum трёх копий совпадает; git diff --stat после build пуст
git diff --check прогнан без ошибок пробелов
node demo/smoke_space_create_display_defaults.mjs прогнан green (OK), проверены все состояния AC1–AC8
node demo/smoke_space_settings.mjs прогнан green (OK), regression не сломан
node scripts/mutation-gate.mjs --check прогнан green, якорь мутанта найден ровно 1 раз
node scripts/mutation-gate.mjs --id=space-create-hidden-display-override прогнан (исполнение) «чистый прогон» зелёный без мутанта, с мутантом — «тест покраснел, как обязан», поймано 1/1

Не прогонялось и почему:

  • npm run golden:verify — диф не меняет геометрию, рендер, стили или слои (только checked-state двух HA switches и порядок записи двух boolean в Save); визуального следствия нет, ТЗ §10 явно исключает golden.
  • Полный набор demo/smoke_*.mjs (127 файлов) — задача касается одной поверхности (create-dialog + onboarding wizard), два целевых смока плюс regression-смок покрывают её; по процессу полный набор — предрелизный гейт, а не гейт ревью.
  • python -m pytest tests_backend -q — диф не затрагивает custom_components/houseplan/**/*.py; backend принимает произвольные bool без изменений (подтверждено в самой ТЗ и не оспаривается диффом).
  • performance-профили — путь не является perf-чувствительным (локальные boolean transitions без I/O), в AC не назван.

Проверка AC (PROCESS §2.7: автотест + доказано, что умеет падать, либо чтение с явной пометкой)

AC Как доказан Итог
AC1 smoke_space_create_display_defaults.mjs: freshFile/untouchedDraw подтверждено исполнением
AC2 тот же smoke: Save сразу после Draw → savedMixed/reopenedMixed (мутантно доказано отдельно на mixed-паре) подтверждено исполнением
AC3 unit space-dialog.test.mjs («every supported Draw pair...») + smoke mixedTouched/savedMixed/reopenedMixed подтверждено исполнением
AC4 smoke untouchedDraw/untouchedFileAgain (до touch) и mixedFile/mixedDrawAgain (после touch) подтверждено исполнением
AC5 smoke onboardingFirst/onboardingSecond — второй этаж не наследует touched первого подтверждено исполнением
AC6 smoke savedFileDefaults (File create) + editSourcePreservesPair (Edit не сбрасывает при смене source) подтверждено исполнением
AC7 smoke cancelDidNotWrite (снапшот _serverCfg до/после Cancel идентичен) + freshAfterCancel подтверждено исполнением
AC8 unit «source and display transitions are immutable and preserve unrelated fields» (title/cellCm через spread) + regression smoke_space_settings.mjs (atticSettings побайтно) подтверждено исполнением
AC9 mutation-gate.mjs --id=space-create-hidden-display-override: старый hidden override восстановлен патчем, гард обязан покраснеть — исполнено, поймано 1/1 подтверждено исполнением, тест доказанно умеет падать
AC10 таблица гейтов выше подтверждено исполнением

Отдельно проверено чтением, не исполнением: путь _warmReviveDialog (§2810) восстанавливает весь снятый снапшот _spaceDialog целиком через spread — поле displayTouched уже присутствует в снапшоте на момент захвата, разрыва контракта при warm-remount нет.

Находки

Нет ни High, ни Medium, ни Low.

Что проверено и корректно

  • Скрытая Save-подмена draw && mode==='create' ? true : d.showBorders (и аналог для show_names) удалена буквально, а не переименована — src/houseplan-card.ts теперь пишет ровно d.showBorders/d.showNames.
  • Общий helper src/space-dialog.ts используется одинаково обычным Create, onboarding (_openNextImport) и радиокнопками — реализация п. «Onboarding расходится с обычным Create» риска (§11 ТЗ) закрыта одной точкой правды, а не дублированием логики.
  • Edit-режим стартует с displayTouched: true, что предотвращает create-only default reset при смене источника существующего пространства.
  • Мутационный тест не просто присутствует «для галочки»: реально исполнен, подтверждает что тест красный без патча (runCleanGuards) и красный именно на мутанте — дисциплина «тест умеет падать» соблюдена.
  • Три копии бандла синхронны, трейлеры и оба changelog в одном коммите — требования AGENTS.md/PROCESS.md выполнены.
  • Терминология docs/USER-GUIDE.ru.md («Без изображения») уже существовала в этом разделе до правки — не изобретена, взята из документа.
  • docs/specs/README.md: порядок строк в таблице был не отсортирован по номеру issue уже до этого коммита (не новая проблема, ссылка на #204 вставлена корректно).

Чего не проверял

  • Полный browser smoke-набор (127 файлов), golden:verify, performance_smoke и backend pytest — не запускались; обоснование см. таблицу гейтов выше. Остаются предрелизным гейтом по процессу.
  • Ручная проверка в реальном браузере (визуальный клик по диалогу) не проводилась — путь доказан Playwright-смоками поверх собранного бандла, что эквивалентно для этой поверхности (нет ручного цикла тестирования на этапе code review по регламенту).
  • Touch/keyboard input specifics (real device) не проверялись — ТЗ §8 утверждает общий change-обработчик для pointer/touch/keyboard, что подтверждено чтением кода (_boolInput — единая точка на <ha-switch>/ <input type=checkbox>), не отдельным touch-тестом.
  • docs/specs/040-floor-area-onboarding.md целиком не перечитывался повторно на этапе code review — при spec review уже подтверждено отсутствие конфликтующих утверждений про defaults; код (_openNextImport) реализует именно контракт §6 ТЗ #204, что и было предметом проверки здесь.