docs: specify persistent alpha switch

Issue: #448
User-Visible: no
This commit is contained in:
Sergey Matyunin
2026-09-04 16:20:34 +03:00
parent 112ec0eb66
commit ed0fdd4859
2 changed files with 261 additions and 0 deletions
+260
View File
@@ -0,0 +1,260 @@
# #448 — Единый бессрочный переключатель `hp_alpha`
- Issue: [#448](https://github.com/Matysh/houseplan-card/issues/448)
- Приоритет: P1
- Тип: feature
- Решения владельца: 2026-09-04, Q1 — default; Q2 — без миграции прежнего `iso`
## Сценарий
Продвинутый пользователь или тестировщик House Plan открывает полную карточку
на своём обычном Home Assistant origin и один раз включает скрытый alpha-режим
через URL. После перезагрузок, перезапусков Home Assistant и обновлений House
Plan ему по-прежнему доступны все экспериментальные функции этой сборки. Сейчас
такой функцией является существующий объёмный вид плана.
## Что человек увидит до и после
До изменения прежний ключ `iso` истёк на версии 1.65, поэтому в актуальной
сборке объёмный вид невозможно включить. После изменения `hp_alpha=1` один раз
возвращает существующий переключатель «Плоский / 3-D» без срока действия;
`hp_alpha=0` выключает весь экспериментальный набор. Старое включение `iso`
автоматически не переносится — режим нужно активировать заново.
## Проблема
`src/labs.ts` сейчас предоставляет внешний реестр отдельных feature-id с
`since`/`expires`, читает операции `hp-labs=iso` и хранит массив id в
`houseplan_card_labs_v1`. Единственный зарегистрированный `iso` истекает перед
версией 1.65.0, поэтому на линии 1.71 он всегда отфильтровывается, хотя
изометрический renderer, выбор вида по пространству и его тестовые сценарии
остались в продукте.
Такой контракт также требует придумывать пользователю новый внешний ключ и
продлевать окно при каждой следующей экспериментальной функции. Решение
владельца заменяет его одним browser-local переключателем способности.
## Скоуп
- Один внешний URL-параметр `hp_alpha` со значениями `1` и `0`.
- Один бессрочный persisted switch на текущем browser/HA-origin.
- Включение одним switch всех экспериментальных функций, присутствующих в
текущей сборке; внутренние идентификаторы функций допустимы только как деталь
реализации и диагностики.
- Возврат существующего объёмного вида и его переключателя в полной карточке.
- Сохранение текущих ограничений объёмного вида: редакторы и
`houseplan-space-card` остаются плоскими, публичного UI включения нет.
- Адаптация unit, browser smoke, golden и performance fixtures к новому
внешнему контракту.
- Актуализация документации Labs/изометрии и двух changelog.
## Не-скоуп
- Публичный запуск 3-D или обычная настройка alpha в интерфейсе.
- Реализация Stage 3 из #160 либо изменение визуального результата Stage 1/2.
- Отдельные пользовательские ключи для `iso` или будущих экспериментов.
- Серверное хранение, синхронизация между браузерами или HA-пользователями.
- Изменение схемы плана, backend API, HA-запросов, сохранения конфигурации,
действий устройств или статической карточки пространства.
- Перенос ранее сохранённого `iso` в новый переключатель.
## Контракт поведения
### Единый внешний переключатель
1. `?hp_alpha=1` включает alpha для текущего browser/HA-origin и запоминает
включение.
2. `#hp_alpha=1&space=<id>` делает то же, не ломая существующий выбор
пространства из hash.
3. `hp_alpha=0` в query или hash выключает alpha и запоминает выключение.
4. URL не переписывается после чтения параметра.
5. Если параметр указан и в query, и в hash, операции применяются в порядке
query → hash: последнее распознанное значение в hash сильнее query. Внутри
одной части URL последнее распознанное значение сильнее предыдущего.
6. Только точные строки `1` и `0` распознаются. Явное иное значение не может
включить alpha: для текущего разрешения оно даёт выключенное состояние и не
перезаписывает storage. Отсутствие параметра читает сохранённое состояние.
7. Отсутствующие или повреждённые persisted-данные дают выключенное состояние.
8. Версия карточки не участвует в решении. Нет `since`, `expires` и
автоматического выключения после beta/stable update.
### Доступные эксперименты
9. При включённом alpha внутренний runtime активирует весь известный этой
сборке экспериментальный набор. В первой поставке набора ровно одна функция:
`iso`.
10. Потребители могут продолжать различать функции внутренними id, но URL,
storage и пользовательская документация не позволяют выбирать их по одной.
11. При выключенном alpha effective projection всегда Flat, даже если
per-space preference хранит `iso`.
12. Выключение alpha не стирает `houseplan_card_view_v1`: повторное включение
возвращает ранее выбранную проекцию пространства. Это состояние вида, а не
второй экспериментальный ключ.
13. В полной карточке View включённый alpha возвращает существующий
переключатель Flat ↔ 3-D. В kiosk применяется сохранённая проекция, но новый
служебный контрол не появляется. В любом редакторе и
`houseplan-space-card` объёмная проекция отсутствует.
14. Любой сбой разрешения alpha безопасно оставляет публичный Flat и не влияет
на данные или HA-команды.
## URL, storage и совместимость
- Новый storage key: `houseplan_card_alpha_v1`.
- Канонические значения storage: строка `1` для enabled и строка `0` для
disabled. Любое другое содержимое считается выключенным.
- Legacy URL `hp-labs=iso`, `hp-labs=-iso`, `hp-labs=off` больше не управляет
функциями.
- Legacy key `houseplan_card_labs_v1` не читается, не мигрируется и сам по себе
не удаляется. Даже точное прежнее значение `["iso"]` не включает alpha.
- Старому тестировщику необходимо один раз открыть карточку с `hp_alpha=1`.
- Per-space preference `houseplan_card_view_v1` сохраняет существующий формат и
не мигрируется.
- Состояние локально для origin и профиля браузера; private mode/quota ошибки
не роняют карточку, но сохранение между сессиями в таком окружении не
гарантируется.
## UX и диагностика
- Обычный пользователь не видит новой настройки, баннера или подсказки.
- Способ включения остаётся намеренно скрытым и документируется только в
материалах разработчика/изометрии.
- Когда alpha включён и экспериментальный кадр действительно отрисован,
dev-console один раз на фактический набор сообщает, что активен `hp_alpha`, и
перечисляет внутренние функции с issue, но без срока истечения.
- Когда alpha выключен либо не удалось разобрать, служебный лог не обещает
активный эксперимент.
- Диагностический browser hook должен однозначно позволять smoke-тесту отличить
alpha enabled от disabled и увидеть фактически активный внутренний набор.
## Модель данных и миграция
План, backend и пользовательская конфигурация не меняются. Новое состояние —
один локальный boolean-like переключатель браузера. Автоматической миграции нет
по прямому решению владельца: legacy storage остаётся неактивным входом, а
пользователь заново подтверждает участие явным `hp_alpha=1`.
Внутренняя модель может сохранить snapshot со списком активных возможностей,
если это уменьшает дифф потребителей. Его источник обязан быть один: при alpha
off список пуст, при alpha on он равен полному набору сборки. Внутренний список
не является persisted или URL API.
## i18n и accessibility
Новых публичных строк и интерактивных элементов нет. Существующие
локализованные названия Flat/3-D, `aria-label` и keyboard contract
переключателя сохраняются без изменений. Kiosk и static card не получают новых
focus targets.
## Критерии приёмки
- **AC1.** `hp_alpha=1` в query и hash включает alpha, сохраняет `1` и не
переписывает URL. Доказательство: unit resolver/storage + browser smoke после
reload.
- **AC2.** `hp_alpha=0` выключает alpha, сохраняет `0`, немедленно возвращает
Flat и убирает переключатель 3-D. Доказательство: unit + browser smoke.
- **AC3.** Query применяется раньше hash; последнее распознанное значение
детерминированно побеждает. Доказательство: табличный unit-тест конфликтов.
- **AC4.** Отсутствующий storage, повреждённый storage и явное неизвестное
значение не включают alpha и не вызывают исключений. Доказательство: unit.
- **AC5.** Поведение одинаково на текущей beta, stable и произвольной будущей
semver: version expiry отсутствует. Доказательство: unit с несколькими
версиями и source-contract без `since`/`expires` в решении.
- **AC6.** `houseplan_card_labs_v1: ["iso"]` и любой `hp-labs=...` не включают
alpha и не переносятся. Доказательство: unit + browser smoke чистого профиля.
- **AC7.** При alpha on существующий полный View предлагает Flat ↔ 3-D и
отображает Stage 2; при alpha off публичный View остаётся Flat.
Доказательство: targeted isometric smoke и существующие golden.
- **AC8.** Сохранённая per-space проекция переживает alpha off/on, но без alpha
никогда не отрисовывается. Доказательство: unit/browser smoke.
- **AC9.** Редакторы и `houseplan-space-card` остаются плоскими, kiosk не
получает служебный контрол. Доказательство: contract unit + smoke.
- **AC10.** Все isometric golden-сценарии явно включают общий alpha и падают,
если фактически получили Flat. Доказательство: matrix unit +
`npm run golden:verify`.
- **AC11.** Isometric performance profile включает alpha новым контрактом и не
использует legacy storage/URL. Доказательство: source-contract + профильный
запуск.
- **AC12.** Диагностика однозначно показывает switch и активный набор без
`expires`; повторный одинаковый кадр не спамит console. Доказательство: unit
или targeted browser smoke.
- **AC13.** Alpha не участвует в HA data/request paths, schema и writes.
Доказательство: static contract tests и чтение границ импорта.
## План автотестов
### Unit и contract
- Переписать таблицу `test/labs.test.mjs`: `1`/`0`, query/hash precedence,
повторные значения, storage persistence, corruption, future versions,
private-storage failures и отсутствие legacy migration.
- Зафиксировать один внешний URL/storage contract; запретить возвращение
`hp-labs`, persisted массива и expiry как активирующих входов.
- Обновить `test/isometric-contract.test.mjs` и golden-matrix contract: full
card использует alpha, secondary card не знает про него, isometric scenarios
объявляют его явно.
### Browser и visual
- Обновить `demo/smoke_isometric_contract.mjs`: clean off → URL on → reload →
3-D → URL off → Flat → повторное on восстанавливает preference.
- Обновить `demo/smoke_isometric_live_touch.mjs` и все fixtures, которые сейчас
подменяют `hp-labs`/legacy storage напрямую.
- Прогнать выбранные `smoke-select` сценарии и весь isometric smoke-контракт.
- Прогнать `npm run golden:verify`; новые эталоны не принимаются, если пиксели
Stage 1/2 не изменились.
### Производительность и основные гейты
- `npm run benchmark:large-house-isometric` с alpha fixture и существующим
бюджетом; задача меняет gate, а не геометрию/рендер, поэтому новый бюджет не
вводится.
- Перед код-ревью: typecheck, unit, build + bundle-tree/budget,
`check-docs`, `no-new-any`, выбранные smokes и golden.
## Затронутые файлы и модули
Ожидаемо: `src/labs.ts`, потребители Labs в `src/houseplan-card.ts`, unit и
isometric browser/golden/performance harnesses, `docs/DEVELOPMENT.md`,
`docs/ISOMETRIC.md`, архитектурные/ADR ссылки при необходимости, оба changelog.
Backend, schema плана и secondary-card renderer изменяться не должны.
## Риски
- Legacy фикстура может продолжить принудительно подставлять `['iso']` и дать
ложнозелёный тест, не проверяющий новый пользовательский вход.
- Стирание projection preference при `hp_alpha=0` превратит временное
выключение экспериментов в потерю пользовательского выбора.
- Параллельные module instances могут зарегистрировать несколько listeners;
существующий single-listener contract нужно сохранить.
- Ошибка precedence query/hash сделает включение зависимым от порядка роутера HA.
- Слишком широкая замена термина `iso` может затронуть внутренний renderer;
меняется capability gate, а не имя проекции.
## Откат
Откат изменения возвращает старый истёкший Labs resolver; публичный Flat при
этом остаётся безопасным. Новый storage key можно оставить неиспользуемым: он
не является данными плана и не требует destructive cleanup. Нельзя откатывать
через продление `iso` expiry или возвращение нескольких внешних ключей — это
восстановит проблему задачи.
## Release-артефакты
- Записи со ссылкой на #448 в `docs/CHANGELOG.ru.md` и `docs/CHANGELOG.md` в
коммите пользовательского изменения.
- Актуальные `docs/DEVELOPMENT.md` и `docs/ISOMETRIC.md`; при изменении
архитектурной границы — соответствующие ADR/`docs/ARCHITECTURE.md`.
- Обновлённые unit, smoke, golden matrix и isometric performance fixtures.
- Обычные синхронные release bundles; новая картинка документации или новый
golden baseline не ожидаются, пока пиксельный результат Stage 1/2 не менялся.
## Принятые предположения — можно менять на ревью без решения владельца
- Storage key — `houseplan_card_alpha_v1`, значения — строки `1`/`0`.
- Явное неизвестное URL-значение выключает alpha только для текущего разрешения
и не портит ранее сохранённое значение; распознанные `1`/`0` записываются.
- Legacy storage не удаляется автоматически: он просто перестаёт быть входом.
- Внутренние feature-id можно сохранить для маршрутизации и диагностики, если
снаружи существует только один alpha switch.
- Отключение alpha скрывает эксперимент, но не стирает per-space projection
preference.
+1
View File
@@ -174,6 +174,7 @@ GitHub Issues и GitHub Projects (v2) остаются единственным
| [#440](https://github.com/Matysh/houseplan-card/issues/440) Полиш аудита v1.71.0-beta.2 | [440-v171-beta2-polish.md](440-v171-beta2-polish.md) |
| [#445](https://github.com/Matysh/houseplan-card/issues/445) Магнит мебели к физической поверхности стены | [445-furniture-wall-face-snap.md](445-furniture-wall-face-snap.md) |
| [#447](https://github.com/Matysh/houseplan-card/issues/447) Наружная грань для мебели и сдвиг декора стрелками | [447-exterior-furniture-snap-keyboard-nudge.md](447-exterior-furniture-snap-keyboard-nudge.md) |
| [#448](https://github.com/Matysh/houseplan-card/issues/448) Единый бессрочный переключатель `hp_alpha` | [448-alpha-switch.md](448-alpha-switch.md) |
## P3