Files
houseplan-card/docs/reviews/SPEC-REVIEW-402-r2.md
T
2026-08-31 13:28:51 +00:00

211 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC-REVIEW-402-r2
- Issue: https://github.com/Matysh/houseplan-card/issues/402
- Артефакт ТЗ: `docs/specs/402-confirm-outside-main-branch.md` (полный трек, класс A)
- Заход: r2 · лимит циклов ревью ТЗ для полного трека — 4 (§4), блокирующих
циклов израсходовано 1/4 (зелёные вердикты бюджет не тратят, #227)
- Ревьюер: Claude (роль «ревьюер ТЗ»), независимая сессия
- Раунд r1: вердикт красный, `docs/reviews/SPEC-REVIEW-402-r1.md`, база
`d94db87e` (коммит, добавивший спецификацию)
- Раунд r2: правка ТЗ — коммит `11959e4c` («docs: #402 spec revision 2 per
SPEC-REVIEW-402-r1»), HEAD ревью — `11959e4c`
## Скоуп ревью (§2.10, по дельте)
r1 закончился красным (High: 1, Medium: 1). Дельта этого раунда —
`git diff d94db87e..11959e4c -- docs/specs/402-confirm-outside-main-branch.md`:
правка ровно двух мест ТЗ (текст «Touch и kiosk» + переформулировка
обоснования не-скоупа для `_tapConfirm`/`_vacCalConfirm`), плюс AC8, AC9 и
два пункта плана автотестов. Никакого продуктового кода в дереве нет (этап —
ТЗ), контракт поведения не изменился, новая подсистема не задета, объём
дельты явно меньше исходной задачи → разбор ограничен дельтой плюс
проверкой, что она не сломала уже принятое (§2.10 п.4-5).
## Как проверялось
1. Прочитан весь диапазон изменений r1→r2 (`git diff d94db87e..HEAD`),
построчно, для файла ТЗ.
2. Найден вердикт r1 и SHA, на котором он получен (`d94db87e`, назван в
шапке `SPEC-REVIEW-402-r1.md` — не пришлось восстанавливать).
3. По каждой находке r1 (H1, M1) сверено текстом дельты, чем именно она
закрыта — раздел «Закрытие раунда r1» ниже.
4. Технические утверждения новой дельты сверены с текущим деревом на
`11959e4c` (код не менялся с `d94db87e`, но проверка не наследуется
автоматически — новый текст мог сослаться на код неточно):
- структура `render()` (`src/houseplan-card.ts:11165-11878`) — семь
ранних `return`, отсутствие `return` между `:11248` и `:11872`
(`awk`-проверка по диапазону) — подтверждает утверждение M1, что
`_tapConfirm` (`:11852`), `_vacCalConfirm` (`:11802`) и `_dangerConfirm`
(`:11872-11878`) лежат в одной и той же финальной ветке без разрыва;
- `docs/TOUCH-SUPPORT.md` § «Safety floor that still applies to touch
editors» (`:69-79`) — сверено число пунктов и их содержание против
фразы ТЗ «запрещает best effort в трёх вещах» — см. находку L1;
- `docs/TOUCH-SUPPORT.md` § «Testing and release gates» (`:139-151`) —
сверено, что touch-editor safety floor входит в release-blocking
гарантии (п.4), а не только View/kiosk (п.2) — это меняет оценку
находки H1 (см. ниже);
- `hasTouch` — паттерн существующих touch-смоков (`grep -rn hasTouch
demo/smoke_*.mjs`) реально существует и используется ровно так, как
описывает AC8 (`demo/smoke_color_picker.mjs`,
`demo/smoke_isometric_live_touch.mjs`, `demo/smoke_touch_tips.mjs`);
- смоки, на которые ссылается AC9 (тап и калибровка пылесоса),
существуют: `demo/smoke_tap_run.mjs`, `demo/smoke_tap_ctx.mjs`,
`demo/smoke_vacuum.mjs`, `demo/smoke_vacuum_firstuse.mjs`,
`demo/smoke_cold_view_vacuum.mjs`.
5. Проверено, остаются ли §7.1-обязательные разделы на месте после правки —
да, ни один не удалён, добавился один новый («Touch и kiosk»).
6. Гейты кода не гонялись — на этапе ТЗ продуктового диффа нет (см. «Чего не
проверял»).
## Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| **H1** (High) — ТЗ не называет влияние на touch/kiosk | Добавлен раздел «Touch и kiosk»: цитирует `TOUCH-SUPPORT.md` § Safety floor, называет её блокирующей для touch, объясняет, почему перенос точки рендера не меняет touch-поведение (тот же компонент, разметка, scrim, футер не меняются), добавлен AC8 (смок в touch-эмуляции на буквальном сценарии issue) | `docs/specs/402-confirm-outside-main-branch.md`, раздел «Touch и kiosk» (строки ~112-127 текущей редакции), AC8 |
| **M1** (Medium, в скоупе) — ложное обоснование «свои ветки» для `_tapConfirm`/`_vacCalConfirm` | Формулировка заменена на две настоящие причины: (1) нет промиса — синхронный `exec()` / закрытие по `hp-close`; (2) точка входа недостижима из ранних веток. Добавлено явное требование к реализации не трогать расположение этих двух блоков при выносе `_dangerConfirm`, плюс AC9 фиксирует это как критерий приёмки | `docs/specs/402-confirm-outside-main-branch.md`, раздел «Скоуп / не-скоуп» (абзац «`_tapConfirm` и `_vacCalConfirm` — не в скоупе...»), AC9 |
M1 закрыта полностью и точно — построчная сверка (`:11802`, `:11852`,
`:11872-11878`, отсутствие `return` между ними) подтверждает переформулировку
слово в слово.
H1 закрыта по существу, с одной оговоркой (см. находку L1 ниже): текст
верно поднимает safety floor и даёт достаточное объяснение для
блокирующей части DoR (View/kiosk), но не называет явно 5 call site'ов
`_confirmDanger` в `houseplan-editor-runtime.ts`, про которые r1 просил
отдельную строку `Touch editor: …`. Разобрано отдельно ниже, почему это не
держит вердикт красным/жёлтым.
## Унаследовано из r1
Без повторной проверки принято всё, чего дельта не касалась — документ
`docs/reviews/SPEC-REVIEW-402-r1.md`, SHA `d94db87e`:
- диагноз дефекта (структура `render()`, семь ранних веток, `hp-confirm`
только в финальной) — построчно сверен в r1, дельта эти строки не меняла;
- корректность `HpConfirmController` (`cancel`/`resolve`/токен) —
`src/danger-confirm.ts` не тронут дельтой;
- буквальный сценарий issue (`_deleteServerPlan` →
`houseplan-onboarding-runtime.ts:218-224,273`) — не тронут;
- факт «`noChange` нигде не оборачивается в шаблон» — граница ТЗ для
языкового гейта `warm`, код не менялся;
- AC1-AC4, AC6 — проверяемость и реалистичность доказательства
(`demo/smoke_danger_confirmation.mjs`) — текст этих AC дельта не
меняла;
- AC5 — рассуждение про недостижимость `!hass/!_config` после первого
монтирования и про то, что `noChange` не трогает уже открытый диалог;
- AC7 — `hp-confirm` импортируется не лениво (`houseplan-card.ts:14`),
бюджет не растёт;
- граница скоупа с #32 (содержимое диалога, ревалидация после `await`) и с
#406 (`alertdialog`/`aria-describedby`) — абзац не менялся;
- соответствие `docs/SCOPE.md` (J4, J6) — не затронуто дельтой;
- присутствие всех обязательных разделов §7.1, кроме нового «Touch и
kiosk» — остальные разделы не удалялись и не переписывались.
## Находки
### L1 (Low, наблюдение — не блокирует). Число в цитате TOUCH-SUPPORT.md неверно
**Где**: `docs/specs/402-confirm-outside-main-branch.md`, раздел «Touch и
kiosk»: «`docs/TOUCH-SUPPORT.md` § Safety floor запрещает «best effort» в
**трёх** вещах».
**Проверено чтением**: `docs/TOUCH-SUPPORT.md:71-79` перечисляет **шесть**
пунктов под «"Best effort" never permits:» — потерю данных, небезопасный
вызов HA, обход подтверждения, залипание карточки вне View, поломку View/
kiosk соседним редактором, случайное сохранение геометрии от неверно
понятого жеста. Ни разу не три.
**Последствие**: не меняет вывод ТЗ — пункт «обход подтверждения
разрушающего действия» реален и процитирован верно, вывод («задача поднимает
пол обратно») не зависит от точного числа. Это неточная цифра в
пересказе документа, а не выданная за факт догадка о продуктовом поведении.
**Решение ревьюера**: снимаю как Low с записью, не блокирует. Автору стоит
поправить «трёх» на «шесть» (или убрать число вовсе) в следующей правке,
которую он уже будет делать по другому поводу — заводить цикл ради одного
слова нецелесообразно (тот же принцип, что «r2 по #150» в §2.10).
### L2 (Low, наблюдение — не блокирует). Явного упоминания touch-статуса пяти вызовов из `houseplan-editor-runtime.ts` по-прежнему нет
**Где**: раздел «Touch и kiosk» рассуждает в общем виде («тот же компонент,
разметка не меняется») и доказывает это AC8, но AC8 сформулирован только
для ветки онбординга; ни разу не назван `houseplan-editor-runtime.ts` и
пять его вызовов `_confirmDanger`.
**Почему это не блокирует, хотя r1 просил именно это**. Я перепроверил
довод по существу, а не только по форме:
- `docs/TOUCH-SUPPORT.md:139-151` («Testing and release gates») перечисляет
четыре release-blocking гарантии, и «touch-editor safety floor» — в их
числе (п.4), не только View/kiosk (п.2). Так что для полноты формально
стоило бы явно назвать и редакторские call site'ы, как просил r1.
- Но по коду («Контракт» ТЗ + AC1, AC2, AC4, AC5) `hp-confirm` выносится из
цепочки `render()` **как единый механизм**, не привязанный к тому, кто
вызвал `_confirmDanger`. AC4 («Открытое подтверждение переживает смену
ветки») сформулирован без привязки к вызывающему — он покрывает и случай,
когда подтверждение открыл редакторский call site, а карточка тем временем
потеряла последнее пространство (`model.length` дошёл до нуля во время
открытого диалога — ровно тот сценарий, который сам текст ТЗ называет
«второй половиной дефекта»). Отдельного AC для редакторских call site'ов
не нужно ровно потому, что контракт написан на уровне компонента, а не
вызывающего.
- Поэтому вывод раздела «Touch и kiosk» («разметка, размер целей нажатия,
scrim, футер не меняются ни на строку») уже покрывает и эти пять
call site'ов — только не называет их явно. Это пробел в полноте текста,
не в контракте: настоящей неопределённости, которая изменила бы AC или
поведение, за этим не стоит.
**Решение ревьюера**: снимаю как Low, не как Medium — субстанция уже
доказана на уровне механизма (AC1-AC5), формальная строка `Touch editor:
supported` для пяти call site'ов была бы уместна для полноты, но её
отсутствие не оставляет открытого продуктового вопроса и не меняет объём
реализации. Не завожу повторный цикл ради одной строки текста (тот же
принцип §2.10, что и для L1). Рекомендация автору — добавить эту строку
попутно при следующей правке, не обязательно сейчас.
## Что проверено и признано корректным (дельта r2)
- Раздел «Touch и kiosk» по существу верно определяет, что затронутая
ветка (View-диалог онбординга) относится к «Fully supported» категории
`TOUCH-SUPPORT.md` и что перенос точки рендера сам по себе не меняет
touch-поведение компонента.
- AC8 технически обоснован: паттерн `hasTouch` реален и используется в
существующих touch-смоках так же, как описано в АС.
- AC9 корректно фиксирует инвариант «блоки на месте» — построчная сверка
подтверждает, что все три диалога (`_tapConfirm`, `_vacCalConfirm`,
`_dangerConfirm`) в текущем дереве действительно в одной ветке без
`return` между ними, то есть требование реализации не тащить их за
компанию — обоснованное и проверяемое.
- M1 закрыта полностью и точно, без остаточных вопросов.
- Новый раздел не удаляет и не противоречит ни одному из ранее принятых
разделов ТЗ (§7.1 набор полон).
- Пункты 6-7 плана автотестов (touch-повтор сценария 1, проверка «один
`hp-confirm` в DOM») соответствуют AC8 и риску «двойной рендер», названному
в разделе «Риски» ещё в r1.
## Чего не проверял
- **Гейты кода** (`npx tsc --noEmit`, `npm test`, `npm run build`,
`check-docs`, смоки, `bundle:budget`, инварианты модели) — не гонялись:
на этапе ТЗ продуктового кода по-прежнему нет, дифф пуст. Это предмет
код-ревью после реализации.
- **`scripts/mutation-gate.mjs` / `demo/smoke_danger_confirmation.mjs`** —
не запускал; в r2 они не менялись, наследую проверку существования файлов
из r1.
- **Реальный браузерный рендер** — не переисполнял; воспроизведение из
аналитики issue взято на веру, как и в r1 (это будет предметом
смок-доказательства AC1-AC9 в код-ревью).
- **`docs/specs/README.md`** — строка для #402 по-прежнему не добавлена;
унаследованное из r1 решение не поднимать это отдельной находкой
(известный долг §7.3 п.1) остаётся в силе.
## Вывод
Обе находки r1 закрыты по существу (таблица выше). Дельта r2 не вносит
новых Medium/High — только два наблюдения Low (неверное число в цитате
документа, отсутствие явной строки про touch-статус пяти редакторских
call site'ов), оба сняты ревьюером с записью и не блокируют выход в
«Готово к разработке».
**Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0**