# SPEC-REVIEW-448-r1 - Issue: [#448](https://github.com/Matysh/houseplan-card/issues/448) — Labs: единый бессрочный переключатель `hp_alpha` для экспериментальных функций - ТЗ: `docs/specs/448-alpha-switch.md` (SHA `aa72704a`) - Трек: полный (лёгкий явно отклонён аналитикой: «меняются долговременный compatibility-контракт и несколько runtime/test/documentation поверхностей») - Этап: `S4-spec-review` · заход r1 · блокирующих циклов израсходовано 0 из 4 (до этого вердикта) - Ревьюер ≠ автор ТЗ (Codex); эта сессия не участвовала в написании ТЗ. ## Скоуп Диапазон, вынесенный на ревью, — только документация: `git diff origin/dev...HEAD` даёт два файла, `docs/specs/448-alpha-switch.md` (новый, 260 строк) и одну строку в `docs/specs/README.md` (реестр ссылок issue↔ТЗ). Продуктовый код не менялся — верно для стадии `S4-spec-review`, где менять `src/**`/ `custom_components/**` запрещено правилом №1 до `S5-ready`. Коммит `aa72704a` несёт трейлеры `Issue: #448` и `User-Visible: no` — корректно, поведение ещё не изменилось. Задача продолжает прямо одобренное узкое 2.5D-исключение из `docs/SCOPE.md` (единственный прецедент `#89`) и служит J1: она не строит новую функцию, а чинит контракт включения уже принятого Stage 1/2 изометрического View, испорченный version-expiry в `src/labs.ts`. Из «не-скоуп» разделов SCOPE.md задача не задевает ничего — доступ к experimental-переключателю остаётся скрытым, публичного 3-D запуска и новых редакторов ТЗ не вводит. ## Как проверялось 1. Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` (§1–§10.4 включительно). 2. Прочитаны тело issue #448 и все три комментария (аналитика, вопросы владельцу, решения владельца со ссылкой на ТЗ). 3. Прочитан канонический `docs/ISOMETRIC.md` (текущий контракт `iso`/Labs, which `hp_alpha` заменяет) и `docs/UX-MODES.md` (раздел Kiosk mode) — подсистема, которую задевает пункт «full-card View и kiosk gate». 4. Прочитан действующий `src/labs.ts` целиком и потребители Labs в `src/houseplan-card.ts` (grep по `iso|Labs|labs`, ~40 точек использования: `_labsIso`, `_desiredProjection`, `_setProjection`, `_effectiveProjection`, projection-toggle кнопка, `noteLabsRender`) — чтобы отличить утверждения ТЗ о текущем поведении от догадок. 5. Сверены i18n-ключи `view.flat`/`view.volumetric` в `src/i18n/{ru,en}.json` с прозой ТЗ («Плоский / 3-D»). 6. Проверено `docs/CONFIG-COMPATIBILITY.md` (сфера действия — persisted-поля схемы плана, не browser-local Labs storage) — подтверждено, что раздел «Модель данных и миграция» ТЗ корректно объявляет её вне применимости этого реестра. 7. Проверено `docs/TOUCH-SUPPORT.md` (product contract: View — touch-first, kiosk — primary supported environment) на предмет обязательного пункта DoR §2.5 «влияние на touch… View и киоск — блокирующие». 8. Проверены обязательные разделы §7.1 построчно по тексту ТЗ. 9. `git show aa72704a --stat` и `-s --format=full` — трейлеры и состав коммита. Гейты кода (`typecheck`/`test`/`build`/`check-docs`) не прогонялись: диапазон не касается `src/**` и `custom_components/**`, только `docs/**`; на этой стадии они не гейт ревью ТЗ. `check-docs.mjs`, о котором автор отчитался в issue, тоже не относится к «правкам src» — но и не вредит, лишний прогон. ## Находки ### Medium (в скоупе задачи — правится в текущем ТЗ) **M1. ТЗ не закрывает обязательный пункт DoR «влияние на touch» (`docs/TOUCH-SUPPORT.md`).** - Файл: `docs/specs/448-alpha-switch.md` - PROCESS.md §2.5 требует для перехода в «Готово к разработке»: «влияние на touch по `docs/TOUCH-SUPPORT.md` (View и киоск — блокирующие)», названо явно или явным «нет». `docs/TOUCH-SUPPORT.md` фиксирует View как **touch-first, обязательный к полной поддержке**, а kiosk — как «primary supported environment»: любой тач-дефект в этих поверхностях — не допустимая деградация, а дефект продукта. - В тексте ТЗ (проверено построчным поиском `touch|Touch|сенсор` — ноль совпадений) нет ни одной строки о touch. Задача прямо называет затронутой поверхностью «full-card View и kiosk gate» (аналитика) и пункт 13 контракта возвращает в View именно интерактивный контрол — существующую кнопку `projection-toggle` (`src/houseplan-card.ts:11501-11507`), которая сейчас недостижима из-за `expires: 1.65.0` и станет достижимой снова. - Это не открытый продуктовый вопрос владельцу: сама кнопка и её touch-поведение уже прошли ревью в #89/#122 и код не меняется — задача лишь восстанавливает достижимость уже принятого контрола через новый резолвер. Достаточно одной явной строки в ТЗ (например, в «UX и диагностика» или отдельным подпунктом «Touch»), фиксирующей это рассуждение как решение ревью, а не оставляющей пункт чек-листа непроверенным. Без такой строки раздел DoR формально не закрыт, и это факт, а не стилистика: чек-лист §2.5 зовёт этот пункт поимённо и отмечает его блокирующим для View/kiosk. - Воспроизведение: `grep -inE "touch|Touch|сенсор" docs/specs/448-alpha-switch.md` → нет совпадений. - Не блокирует переход High-порогом, чинится добавлением строки в этом же ТЗ, отдельный цикл не обязателен по существу (это не смена контракта), но по процессу возврат один — правка ТЗ и повторный (лёгкий) заход. ## Что проверено и корректно - **Обязательные разделы §7.1** все присутствуют и в правильном порядке: сценарий, что человек увидит до/после, проблема, скоуп/не-скоуп, контракт поведения, UX и диагностика, модель данных и миграция, i18n и accessibility, критерии приёмки AC1–AC13 с доказательством, план автотестов, риски, откат, release-артефакты, плюс необязательный, но полезный блок «Принятые предположения». - **AC1–AC13** пронумерованы, взаимно не пересекаются и у каждого указан явный способ доказательства (unit / unit+smoke / source-contract / targeted smoke + golden / performance profile / static contract). Ни один AC не сформулирован как «работает корректно» без проверяемого критерия. - **Ни одной непомеченной догадки о существующем поведении не найдено.** Каждое фактическое утверждение о текущей системе сверено с кодом/каноном и подтвердилось: version-expiry `1.65.0` действительно отфильтровывает `iso` на линии 1.71 (`liveLabsFlags` + `LABS_FLAGS[0].expires`); переключатель Flat↔3-D уже существует в full-card View и уже скрыт в kiosk (`!this._kiosk`) — `src/houseplan-card.ts:11501`; per-space preference `houseplan_card_view_v1` используется как заявлено (`LS_VIEW` в `houseplan-card.ts:489`); редакторы и `houseplan-space-card` не проецируют iso — подтверждено `docs/ISOMETRIC.md` («Editors and houseplan-space-card are always flat»). Единственная content-догадка о будущем поведении («явное неизвестное URL-значение не портит сохранённое значение») корректно вынесена в блок «Принятые предположения», как и требует PROCESS.md §7.1. - **Продуктовые вопросы владельцу заданы по существу**, каждый с предложенным default (способ включения/выключения `hp_alpha`; судьба legacy `iso`), ответ получен и зафиксирован явным решением в третьем комментарии issue — процессуально верно (§3.7.1: только «что видит/делает человек» и «объём видимых изменений», без технических вопросов владельцу). - **Скоуп/не-скоуп** точно очерчивают границу: явно исключены публичный запуск 3-D, Stage 3 (#160), серверное хранение/синхронизация, миграция старого `iso`. Соответствует ответу владельца на Q2 (без миграции). - **Откат** описан содержательно — включает явный запрет «плохого» отката (продление `iso`-expiry, возврат нескольких внешних ключей), что и было первоисточником проблемы. - **Компат-модель**: раздел корректно утверждает, что схема плана/backend/HA- запросы не меняются — проверено по `docs/CONFIG-COMPATIBILITY.md`, чья область действия — persisted-поля конфигурации плана, а не browser-local Labs-хранилище; обращение к этому реестру здесь действительно не требуется. - **i18n**: заявлено «новых публичных строк нет» — проверено, `view.flat` / `view.volumetric` уже существуют в `ru.json`/`en.json`; прозаические «Плоский / 3-D» в тексте ТЗ — не заявка на новую строку, а неформальный пересказ существующих меток, реальный текст меток задачей не меняется. - **Класс изменений и трейлеры коммита** верны: только `docs/**` (класс C), `Issue: #448`, `User-Visible: no` — корректно для стадии, где поведение ещё не менялось; продуктовый код не тронут ни одним файлом (правило №1 не нарушено). - **Персона/повод**: аналитика прямо связывает задачу с J1 SCOPE.md и с уже одобренным исключением #89; формулировка «продвинутый пользователь/тестировщик» в разделе «Сценарий» не совпадает дословно ни с одной строкой таблицы персон SCOPE.md (`Home admin`/`Household members`/`Guests`), но по контексту (URL-флаг, desktop) однозначно про Home admin — рассмотрено, не поднято до отдельной находки: не создаёт двусмысленности для реализации или проверки AC, чисто формальная формулировка. Low, не фиксирую отдельно. ## Чего не проверял - Не прогонял `npx tsc --noEmit` / `npm test` / `npm run build` — диапазон не содержит кода класса A/B, эти гейты не гейтуют ревью ТЗ. - Не прогонял `node scripts/check-docs.mjs` — диапазон не касается `src/**` (отпечаток скриншотов документации), автор уже привёл `passed` в issue, но это не обязательный гейт для docs-only диффа. - Не проверял golden/smoke/performance-фикстуры буквально (их ещё нет — они появятся в реализации, план автотестов на этой стадии оценивается как план, а не как прогон). - Не оценивал техническую реализуемость внутреннего резолвера построчно (не моя роль на этой стадии и не предмет спор ТЗ↔код) — только соответствие контракта существующему коду и канону. ## Вердикт `hp_alpha` заменяет version-expiry контракт корректно очерченным, непротиворечивым и полностью доказуемым ТЗ; единственная находка — реальный, но локально устранимый пробел DoR по touch-влиянию, входящий в скоуп этой же задачи. Вердикт: жёлтый · заход r1 · блокирующих циклов 1/4 · High: 0 · Medium: 1 → в задаче --- ## Материал раунда - Ветка: `issue/448-alpha-switch`, коммит `aa72704aacce` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `4bea42cb1c9dc9c0fc49c8de4005942035e965d1` ``` git log --all --format='%H %T' | grep 4bea42cb1c9d ``` - ТЗ `docs/specs/448-alpha-switch.md`, блоб `ba9b8e5f621f5ad8ecb881e1cbf40d3cc24573ef` ``` git log --all --find-object=ba9b8e5f621f5ad8ecb881e1cbf40d3cc24573ef -- docs/specs/448-alpha-switch.md ```