diff --git a/docs/reviews/SPEC-REVIEW-448-r1.md b/docs/reviews/SPEC-REVIEW-448-r1.md new file mode 100644 index 00000000..d5ee53de --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-448-r1.md @@ -0,0 +1,181 @@ +# 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 + ```