Files
houseplan-card/legacy/specs/204-space-create-display-defaults.md
Claudeandclaude[bot] df46fd1c3e docs(hygiene): ТЗ выпущенных задач без живых ссылок — в legacy/specs (#682)
Волна 5 эпика #674, перенос ТЗ (класс C). Из 240 файлов `docs/specs/` в
`legacy/specs/` уехали 219: на них не ссылается ни один живой файл (код,
тесты, скрипты, workflow, документы вне архива и ревью). Остались 21 ТЗ —
на которые ссылаются код, ADR, ISOMETRIC, SUN, RADAR, LIGHT (`docs/specs/067`),
DECOR-EDITOR, support-relay, и те, на которые ссылаются они сами; README
каталога объясняет, где искать остальное. Открытых issue с файлом ТЗ среди
перенесённых нет. Относительные ссылки перенесённых файлов переписаны
(`../X` → `../../docs/X`, соседние оставшиеся ТЗ → `../../docs/specs/…`) —
все 26 резолвятся. Попутно: битая ссылка в
`089-isometric-view-stage1.md:8` на удалённый `089-isometric-view.md` —
теперь команда `git show` по истории. Строка в `legacy/README.md`.

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:46 +00:00

13 KiB
Raw Permalink Blame History

Issue #204 — честные defaults границ и имён при создании пространства

  • Дата: 2026-08-19
  • Тип: bug · приоритет P2
  • Оценка: пользовательская ценность 7/10 · ценность для разработки 6/10 · сложность 4/10 · риск 5/10
  • Issue: #204
  • Ветка: issue/204-space-create-display-defaults

Канонические документы: docs/SCOPE.md, docs/UX-MODES.md, docs/USER-GUIDE.ru.md, docs/TESTING.md, docs/specs/040-floor-area-onboarding.md.

1. Сценарий и персона

Администратор создаёт пространство вручную либо последовательно заводит этажи через Floors/Areas onboarding. В диалоге он выбирает источник «Рисовать комнаты вручную», проверяет настройки отображения и сохраняет. Значения на экране должны быть теми значениями, которые попадут в config и будут показаны при повторном открытии.

2. До и после

До: оба переключателя — «Всегда отображать границы комнат» и «Отображать названия комнат» — видимо выключены. Для create + draw Save игнорирует их и принудительно записывает true/true. Повторное открытие показывает включённое состояние, которого пользователь не выбирал.

После: при выборе ручного пространства оба действующих product defaults сразу видимо включены и именно они сохраняются. Для пространства с файлом исходный default остаётся false/false. Если пользователь изменил хотя бы один из двух переключателей, дальнейшие File ↔ Draw в том же открытом диалоге не перезаписывают ни один из его выборов.

3. Подтверждённая причина

_openSpaceDialog('create') и _openNextImport() создают dialog state с source:'file', showBorders:false, showNames:false. Радиокнопки источника меняют только source. _saveSpaceDialog() отдельно содержит скрытую ветку:

show_borders: draw && d.mode === 'create' ? true : d.showBorders
show_names: draw && d.mode === 'create' ? true : d.showNames

Поэтому только create + draw нарушает round-trip. Create с файлом и Edit уже сохраняют показанные значения; backend принимает явные boolean без подмены. #203 исправляет runtime-смысл show_names, но не dialog defaults и не Save.

4. Scope

  • сделать default-пару источника частью create-dialog state: File false/false, Draw true/true;
  • применять пару при переключении источника, пока пользователь не тронул ни один из этих двух controls;
  • после первого пользовательского изменения сохранять оба текущих значения при любых следующих File ↔ Draw;
  • сохранять в config ровно d.showBorders и d.showNames без скрытой ветки;
  • одинаково применить контракт в обычном Create и Floors/Areas onboarding;
  • проверить Save/reopen, Cancel и source-switch round-trip.

5. Non-scope

  • изменение runtime resolver spaceDisplayOf() или render поведения #203;
  • миграция уже созданных spaces и переписывание explicit/implicit settings;
  • изменение defaults Edit, legacy spaces либо space с уже сохранёнными values;
  • изменение hide_decor, hide_openings, fill/Glow, background, масштаба или других полей диалога при source switch;
  • добавление предупреждения, текста, reset-кнопки или отдельных preferences на каждый source;
  • изменение HA Floors/Areas registry или backend schema/API.

6. Контракт dialog state

Create state получает внутренний ephemeral признак displayTouched (имя может быть другим). Он не сохраняется в config.

  1. Новый обычный Create и каждый новый шаг onboarding открываются как File с видимой парой false/false, displayTouched:false.
  2. Пока displayTouched:false, переход на Draw атомарно ставит true/true, а переход на File — false/false. Повторные переходы воспроизводят default выбранного source и не влияют на прочие поля.
  3. Изменение любого из двух переключателей ставит displayTouched:true. Второй control не меняется автоматически.
  4. При displayTouched:true смена source меняет только source; текущая пара сохраняется целиком. Это даёт в том числе намеренные true/false и false/true.
  5. Save для Create и Edit всегда записывает текущие dialog booleans. Значение не выводится заново из наличия background.
  6. Cancel/close уничтожает ephemeral state. Следующий Create снова начинает с File false/false; следующий этаж onboarding получает собственный чистый state и не наследует touch предыдущего.

Выбор source и изменение control происходят синхронно в одном новом object state, поэтому Lit не может отрисовать промежуточную ложную пару.

7. Данные и совместимость

Persisted schema не меняется: settings.show_borders и settings.show_names уже boolean. Новые hand-drawn spaces по default по-прежнему получают true/true; меняется только честность UI и возможность до Save выбрать другое. Новые file spaces по default по-прежнему получают false/false.

Existing spaces и imports/backups не мигрируются. Edit читает effective values и сохраняет их как раньше; ephemeral touch нужен только create-mode. Downgrade читает те же booleans. Нет нового model/version marker.

8. UX, accessibility, touch и i18n

Состав, порядок и тексты controls не меняются. Обновляются checked states двух существующих HA switches. Keyboard, pointer и touch проходят один и тот же change handler; focus не переносится, live announcement не требуется. Цвет не является единственным сигналом — состояние остаётся нативно доступным через checked/ARIA контракт control. Новых locale keys нет.

9. Acceptance criteria

AC Критерий Доказательство
AC1 Новый Create открывается как File с false/false; первый переход Draw показывает true/true. Targeted dialog smoke + state assertions.
AC2 Save сразу после выбора Draw сохраняет показанные true/true; reopen читает true/true. Production-bundle smoke.
AC3 Пользовательские false/false, true/false и false/true для Draw сохраняются без подмены и совпадают после reopen. Table-driven smoke/unit seam.
AC4 До touch File ↔ Draw переключает false/false ↔ true/true; после touch source меняется, а оба booleans остаются. Source-switch matrix.
AC5 Новый этап Floors/Areas onboarding следует той же матрице и не наследует touch/values предыдущего этажа. Extended onboarding smoke.
AC6 Create с файлом по default сохраняет false/false; Edit существующего пространства не меняет values при open/save. Regression smoke.
AC7 Cancel не пишет config; новый Create возвращает чистый default. Save-call/config snapshot assertions.
AC8 Остальные dialog fields побайтно сохраняются при source transitions; runtime #203 остаётся зелёным. Focused state regression + existing smoke.
AC9 Старый hidden Save override ловится мутантом. Mutation gate.
AC10 Рабочие gates зелёные. typecheck, unit, build, targeted smokes; bundle copies identical.

10. План реализации и тестов

Создание начального state и source transition следует вынести в маленький pure helper либо эквивалентную общую функцию, чтобы обычный Create и _openNextImport не расходились. Handlers двух переключателей помечают pair touched. Save удаляет ветку draw && create ? true : ....

Основной targeted browser сценарий может расширить demo/smoke_space_settings.mjs или быть выделен как smoke_space_create_display_defaults.mjs. Он проходит default, source matrix, mixed values, Save/reopen, Cancel и onboarding. Unit допустим для pure state helper; DOM/Save round-trip остаётся обязательным.

Mutation entry восстанавливает принудительные true в Save либо отключает touched guard и обязан падать на mixed-value/source-switch проверке. Golden не нужен: checked и persisted booleans являются точным доказательством. Полный smoke/golden/performance остаётся предрелизным gate.

11. Риски и меры

Риск Мера
Source switch затирает осознанный mixed choice Один touched guard на пару, AC3/AC4.
Onboarding расходится с обычным Create Общий initializer/transition и AC5.
Touch предыдущего этажа протекает дальше Новый state на каждый _openNextImport().
Исправление меняет существующий product default Persisted default pairs остаются прежними.
Edit получает create-only reset Mode matrix и AC6.

Производительность и security/privacy boundary не меняются: локальные boolean transitions не добавляют I/O, registry access или HA service calls.

12. Release-артефакты и rollback

Изменение пользовательское. Implementation-коммит имеет User-Visible: yes и включает:

  • docs/CHANGELOG.md и docs/CHANGELOG.ru.md со ссылкой на #204;
  • docs/USER-GUIDE.ru.md — defaults File/Draw и сохранение выбора;
  • docs/TESTING.md — source/touch/onboarding matrix;
  • docs/STATUS.md — фактическую release-линию;
  • targeted smoke, mutation entry и три синхронные bundle copies.

Отдельные backend, schema/migration, i18n, screenshot/golden, security и performance artifacts не нужны. Rollback — revert implementation-коммита; данные остаются валидными, но вернётся скрытая Save-подмена для create+draw.

13. Принятые предположения

  1. Один touched flag намеренно защищает пару: изменение одного control означает, что dialog больше не вправе автоматически сбрасывать и второй.
  2. Пока пользователь не менял controls, каждый source switch показывает default текущего source; отдельная память values по источникам не требуется.
  3. Internal helper/field names и выбор smoke-файла являются техническими решениями при сохранении §6.
  4. Продуктовых вопросов больше нет: Q1–Q2 приняты владельцем по defaults.