# SPEC-REVIEW-381-r2 - Issue: https://github.com/Matysh/houseplan-card/issues/381 — «Действие по нажатию: добавить "Ничего не делать"» - Этап: ТЗ на ревью (PROCESS.md §2.4), полный трек (подтверждён в r1, дельтой не затронут) - ТЗ: `docs/specs/381-no-op-tap-action.md` - Материал ревью: ветка `issue/381-no-op-tap-action`, ревизия автора `a64833a8` (комментарий автора называет этот SHA явно) - Заход: r2 · блокирующих циклов израсходовано 1 из 4 ## Предыдущий раунд - Вердикт r1: жёлтый · High: 0 · Medium: 1 → в задаче. - SHA, на котором получен вердикт r1: `7a260e8f` (ревизия ТЗ 1, найден по git log — в самом вердикте SHA не назывался, только «ревизия 1»; это не находка данного раунда, а восстановленный факт, нужный для дельты). - Документ r1: `docs/reviews/SPEC-REVIEW-381-r1.md` (коммит `abbaebbc`). - Единственная находка r1 — **M1 (Medium, в скоупе)**: в ТЗ отсутствовали обязательные разделы `## Риски` и `## Откат` (PROCESS.md §7.1, DoR §2.5). - Снятый Low (L1): ярлык AC4 «visual assertion» вне канонического словаря — не требовал отдельного цикла, но было предложено переименовать заодно. ## Дельта r1 → r2 `git diff 7a260e8f..a64833a8 -- docs/specs/381-no-op-tap-action.md` и `git diff --stat 7a260e8f..a64833a8` (полный репозиторий) показывают: - изменён ровно один файл ТЗ (плюс появление `docs/reviews/SPEC-REVIEW-381-r1.md` — это артефакт самого ревью, не предмет проверки автора); - шапка: «первая редакция» → «вторая редакция, замечание r1 устранено», ревизия 1 → 2; - добавлен раздел `## Риски` (5 пунктов); - добавлен раздел `## Откат` (двухэтапный, с явным разбором downgrade); - AC4: ярлык «unit + visual assertion» → «unit, DOM assertion». Никакие другие разделы (Сценарий, Скоуп/Не-скоуп, Контракт поведения, UX/i18n, Модель данных, Затронутые файлы, AC1–AC9 кроме ярлыка AC4, План автотестов, Производительность, Release-артефакты, Принятые предположения) не менялись. Дельта локальна к требованию M1 — не рёбейз, не смена контракта поведения, не новая подсистема, объём на порядок меньше исходной задачи. Разбор в этом раунде сокращён по дельте (PROCESS.md §2.9); полный список того, что наследуется без повторной проверки, — в разделе ниже. ## Закрытие раунда r1 | Находка | Чем закрыта | Где это видно | |---|---|---| | **M1** — отсутствуют `## Риски` и `## Откат` (§7.1, DoR §2.5) | Оба раздела добавлены с предметным наполнением, привязанным к механизмам контракта, а не общими словами | `docs/specs/381-no-op-tap-action.md:222-266` (см. текст ниже) | | **L1** (Low, был снят без цикла) — ярлык AC4 вне словаря | Автор всё же переименовал попутно, как и предлагалось «если правит документ по M1» | `docs/specs/381-no-op-tap-action.md:180` — «unit, DOM assertion» | Проверка содержания `## Риски` (строки 222–245): 5 пунктов, каждый называет конкретный механизм отказа и снимающий механизм со ссылкой на AC: 1. смешение `none` с absence/falsy → снимается явной веткой и таблицей в AC2/AC5; 2. неполный охват потребителей `tap_action` (fingerprint, virtual-light, cover presentation) → снимается предреализационным поиском consumers и AC4/AC6; 3. ранний return не в той точке относительно `stopPropagation`/fallback → снимается dispatcher-тестом и списком мутантов; 4. mixed-version/downgrade → снимается совместной поставкой frontend+backend и порядком отката, описанным ниже; 5. пассивное нажатие приняли за поломку → снимается тем, что long press/right click остаются документированными, и smoke-проверкой. Это ровно тот уровень конкретности, который требовал r1 («предметные риски конкретно этого контракта», а не общие слова) — риски 2–4 почти дословно покрывают то, что r1 предлагал как пример содержания. Проверка содержания `## Откат` (строки 247–266): двухэтапная схема — (1) revert selector/runtime/i18n/docs, backend allowlist временно оставляет `none` (старый frontend уже безопасно проецирует его в `info` — ссылается на уже проверенный в r1 факт про `projectedTapAction`); (2) отдельный data-fix переписывает сохранённые `none` в `info` перед тем, как убрать literal из backend. Отдельно явно запрещён «слепой» полный `git revert` одним шагом с объяснением, почему он ломает запись уже существующих конфигов старым backend. Раздел отвечает буквально на вопрос DoR §2.5 «как откатить именно этот выпуск» и не оставляет читателю выводить это самому — та же претензия, которая была у r1 к рассеянному по тексту содержанию, снята: раздел самодостаточен, хотя и корректно ссылается на уже существующий раздел Import/export вместо дублирования его целиком. Оба раздела не противоречат остальному контракту: сценарий downgrade в «Откате» согласован с фактами, уже подтверждёнными в r1 чтением исходников (`projectedTapAction` fail-closed в `info`, `MARKER_SCHEMA` — allowlist). Новых утверждений о поведении кода, не проверенных в r1 и требующих сверки с `dev`, раздел не вносит — это узнаваемое пересказывание уже проверенных фактов под правильным заголовком, что и просил r1. M1 закрыт по существу. Новых находок в дельте нет. ## Унаследовано из r1 Без повторной проверки в этом раунде принято (документ `docs/reviews/SPEC-REVIEW-381-r1.md`, SHA ревизии на момент проверки `7a260e8f`, дельта их не затрагивает): - продуктовая рамка и job **J3** `docs/SCOPE.md`, персона и поверхность; - выбор трека (полный, не `small`/`trivial`) по критериям §5; - все фактические утверждения ТЗ о текущем коде (`TAP_ACTIONS`, `projectedTapAction`, `_clickDevice`, `_keyDevice`, `_ctxDevice`/long press, editor selector visibility, lossless untouched-write, `tap_target` очистка, backend `MARKER_SCHEMA`, import_export virtualization, `device-presentation.ts` cover independence, i18n-словари и паттерн ключей, cross-language backend test) — сверены с `dev` в r1, дельта их не меняет; - терминология («Действие по нажатию» из `docs/USER-GUIDE.ru.md:812`, touch как гарантированная поверхность по `docs/TOUCH-SUPPORT.md`); - однозначность и доказуемость AC1–AC9 (кроме переименованного ярлыка AC4, проверенного заново выше) и полнота плана автотестов/раздела «Мутанты»; - полнота «Затронутые файлы и модули»; - release-артефакты (оба changelog в скоупе одного `User-Visible: yes` коммита, отсутствие нового golden/docs-скриншота). ## Гейты Продуктовый код не менялся ни в r1, ни в дельте r2 (`git diff --stat` выше — только `docs/specs/*` и `docs/reviews/*`). Этап — ревью ТЗ: `tsc`/`test`/ `build`/`invariants`/smoke/`golden:verify`/`check-docs` неприменимы и не прогонялись, как и в r1. Они станут обязательны на этапе код-ревью (§2.7). ## Чего не проверял - Не проверялась реализация — её по-прежнему нет. - Не проверялась орфография/стиль переводов DE/FR по существу — не менялись в дельте, а структурная проверка ключей унаследована из r1. - Не выполнялся повторный построчный разбор разделов, не тронутых дельтой (см. «Унаследовано из r1» выше) — это осознанное сокращение объёма по PROCESS.md §2.9, а не пропуск. ## Вердикт M1 закрыт предметно, новых находок дельта не создала, остальной контракт наследуется из r1 без изменений. High: 0, Medium: 0 → зелёный. `Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0 · Документ: docs/reviews/SPEC-REVIEW-381-r2.md`