# #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=` делает то же, не ломая существующий выбор пространства из 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. Влияния на touch-контракт нет: в полном View снова становится достижимой уже принятая в #89/#122 кнопка `projection-toggle`, и её существующие tap, keyboard и focus-поведение не меняются. Kiosk по-прежнему не получает ни эту кнопку, ни новую touch-цель; для View и kiosk не допускается деградация требований `docs/TOUCH-SUPPORT.md`. ## Критерии приёмки - **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.