# SPEC-REVIEW-289-r2 - **Issue:** https://github.com/Matysh/houseplan-card/issues/289 - **Артефакт ТЗ:** `docs/specs/289-no-mixed-role-resize.md` - **Ветка/коммит:** `issue/289-no-mixed-role-resize` @ `e02c282d` (`origin/dev` + 2 коммита сверх r1: `1dae3b0c` — документ ревью r1, `e02c282d` — правки автора; подтверждено `git diff origin/dev..HEAD --stat` — только два docs-файла, продуктовый код не тронут) - **Заход:** r2 · блокирующих циклов израсходовано 1 из 4 - **Вердикт:** зелёный · High: 0 · Medium: 0 · Low: 1 (снят с запиской, не блокирует) ## Скоуп ревью Раунд r1 (см. `docs/reviews/SPEC-REVIEW-289-r1.md`, коммит `5e169f48`) дал жёлтый вердикт: High 0, Medium 3 (M1 «Риски» отсутствует, M2 «Откат» отсутствует, M3 — 6 из 9 AC без способа доказательства), Low 2 (L1 терминология «рукоятка»/«ручка», L2 — i18n-ключ не назван буквально). Технический контракт §2–§4, AC1–AC9 по существу и границы скоупа §5 в r1 уже признаны корректными точным чтением кода и канона. Автор ответил правками в `e02c282d` («докcs: add evidence and rollback to resize spec») и сообщил в issue, что внёс все три Medium. Эта дельта — предмет r2: `git diff 5e169f48..e02c282d -- docs/specs/289-no-mixed-role-resize.md`. Дельта локальна (добавление разделов и строк «Доказательство», без изменения самого контракта, AC-формулировок или скоупа) — полный повторный разбор продукта не требуется, объём разбора сокращён до делты и её последствий. ## Как проверялось 1. Нашёл вердикт r1 в комментариях issue #289 (комментарий от 2026-08-24 10:48:29) и SHA, на котором он получен. В самом комментарии-вердикте SHA не назван — по инструкции это сигнал для проверки; но опубликованный документ `docs/reviews/SPEC-REVIEW-289-r1.md:5` называет его явно: `5e169f48`. Ложной тревоги нет, SHA подтверждён. 2. `git diff 5e169f48..e02c282d -- docs/specs/289-no-mixed-role-resize.md` — построчно, каждый добавленный/изменённый хунк сверен с тем, какую находку r1 он должен закрывать (таблица ниже). 3. `git diff 5e169f48..e02c282d --stat` (весь коммит, не только spec-файл) — подтвердил, что в дельте нет ничего кроме ожидаемого: сам spec-файл плюс документ ревью r1 (публикация предыдущего раунда, не предмет этого ревью). 4. Пересчитал нумерацию разделов файла целиком (`grep -n "^## [0-9]"`) — после вставки §8/§9 разделы 8–12 сдвинулись; последовательность 1…12 без пропусков и дублей, внутренних ссылок `§N` на старые номера в файле нет — реформат не оставил битых ссылок. 5. Проверил заявление о каноническом термине: `docs/USER-GUIDE.ru.md` (раздел Resize, таблица сценариев) называет элемент управления «Ручка», слова «рукоятка» в проекте вне этого ТЗ нет — сверил актуальное количество вхождений в текущей редакции файла (`grep -ni`), а не унаследовал цифру из текста r1. 6. Не прогонял `typecheck`/`test`/`build`/инварианты — `git diff origin/dev..HEAD --stat` показывает только два docs-файла (`docs/specs/289-*.md`, `docs/reviews/SPEC-REVIEW-289-r1.md`), продуктового кода на ветке всё ещё нет. Гейты класса A/B неприменимы к чисто документационному изменению на этапе ТЗ — тот же вывод, что и в r1, дельта его не меняет. ## Закрытие раунда r1 | Находка r1 | Чем закрыта | Где это видно | |---|---|---| | **M1** — раздел «Риски» отсутствует полностью | Добавлен раздел «8. Риски и меры»: три риска (чрезмерно строгий ownership → ложный disable легитимных жестов; чрезмерно мягкий → возврат mixed-role; расхождение owners между preview и commit), каждый со ссылкой на конкретный AC-барьер (AC3/AC4, AC1/AC5/AC8, AC6) | `docs/specs/289-no-mixed-role-resize.md:210-218` | | **M2** — раздел «Откат» отсутствует полностью | Добавлен раздел «9. Откат»: явно зафиксировано «чистый revert коммита», без флага/миграции, обосновано неизменностью persisted geometry/schema — то самое тривиальное решение, которое r1 предсказал, но потребовал записать явно | `docs/specs/289-no-mixed-role-resize.md:220-223` | | **M3** — 6 из 9 AC (AC1–AC6) без способа доказательства | Каждому AC1–AC6 добавлена строка «**Доказательство:** …» с конкретным инструментом (unit в `test/resize.test.mjs`, production-bundle smoke, `scripts/model-invariants.mjs`) — по одной на каждый | `docs/specs/289-no-mixed-role-resize.md:120-121,129-130,139-140,151-152,161-162,171-172` | | **L2** — i18n-ключ не назван буквально | В разделе «Ожидаемые файлы» ключ `resize.disabled.partial-shared` назван по имени, а не только как значение `reason` | `docs/specs/289-no-mixed-role-resize.md:231-232` | | **L1** — «рукоятка» вместо канонической «ручка» | Не исправлено. В §2 (строка 35) и §3 (строка 44) слово «рукоятка» осталось; правки касались только новых разделов и не задели прозу §2/§3 | `docs/specs/289-no-mixed-role-resize.md:35,44` | Уточнение по L1: текст r1 указывал «использовано 5 раз (§2, §3, §4.2×3)» — это не подтвердилось. В §4.2 (строки 64–78) на самом деле используется английское слово `handle` (та же RU/EN смесь технических терминов, что и у «moving edge»/«side edges» по всему документу, которую сам r1 признал соответствующей канону), а не «рукоятка». Фактическое количество вхождений слова «рукоятка» в редакции `5e169f48` и в текущей `e02c282d` одинаково — 2, не 5, и дельта r2 в этом месте нулевая. Обе строки чисто описательные (не согласованный с владельцем disabled-текст AC2, который дословно совпадает с решением владельца и не содержит «рукоятка»/«ручка»). Расхождение с каноническим термином реально, но не влияет на реализуемость или проверяемость ни одного AC — снимаю как Low с запиской, не требую четвёртого цикла ради двух слов в прозе. ## Унаследовано из r1 Без повторной проверки в r2 принято на основании `docs/reviews/SPEC-REVIEW-289-r1.md` (документ на SHA `5e169f48`), так как дельта `5e169f48..e02c282d` не касается этих утверждений: - **Технический контракт §1/§4 (ownership-модель, запрещённая смена роли, thickness preservation)** — соответствие `resolveSafeResize()`/ `validateSafeResize()` в `src/resize.ts` подтверждено чтением кода в r1; дельта не меняет ни одной строки §1/§4. - **Симметричность контракта §4.2** (оба направления жеста небезопасны в exact-репро, mixed-role у соседней комнаты при укорачивании) — геометрически проверено в r1; текст §4.2 не менялся. - **AC1–AC9 по существу** (что каждый критерий требует) — проверено в r1 построчно против кода и канона; дельта добавила только строки «Доказательство:», не изменив ни одной формулировки требования. - **Существование и семантика инструментов**: `checkMixedRoleRecords`, `checkWallRecordsPreserved`, `checkWallKeys`, `checkReferences`, `checkPhysicalGeometry` (`scripts/model-invariants.mjs`), `resize.disabled.partial-shared` и `resize.commit_failed` (оба i18n-файла) — подтверждено чтением исходников в r1, дельта их не касается. - **Границы скоупа §5** (не входит: разрез записи, смена толщины, каскад топологии, рефактор #264, ретро-починка через Optimize, #288, #290) — сверено с историей issue в r1, текст §5 не менялся. - **Продуктовое решение §2 соответствует ответу владельца на Q1** — сверено в r1 слово в слово, текст §2 (кроме уже учтённого L1) не менялся. - **Совместимость §7** (schema/model version не меняются, touch — best effort, безопасный pointermove-путь) — сверено в r1 с `docs/CONFIG-COMPATIBILITY.md` и `docs/TOUCH-SUPPORT.md`, текст §7 не менялся. ## Что проверено и корректно в этом раунде (не находка, а подтверждение) - Все три Medium из r1 закрыты предметно, а не декларативно: каждая правка — конкретный текст в конкретной строке, а не общая фраза «риски учтены» (проверено построчным диффом, см. таблицу выше). - Реформат нумерации разделов (8→10, 9→11, 10→12) не оставил битых внутренних ссылок и не изменил порядок обязательных по §7.1 PROCESS.md разделов. - Раздел «Риски» не декоративен: каждый из трёх рисков привязан к конкретному AC-барьеру, который его закрывает, а не общей фразой «риски будут обнаружены на код-ревью». - Раздел «Откат» согласован с остальным документом: «schema/model version не меняются» из §7 и «persisted-данные не переписываются» из §4.3 — одно и то же утверждение, не новое допущение, придуманное только для раздела 9. ## Чего не проверял - Продуктовый код и тесты — на ветке их всё ещё нет (`git diff origin/dev..HEAD --stat` показывает только два docs-файла); гейты `typecheck`/`test`/`build`/ `invariants`/смоки неприменимы на этапе ТЗ, как и в r1. - Всё, что унаследовано из r1 и перечислено выше отдельным разделом, — не перепроверялось повторно, так как дельта его не задевает. - Формулировка Low L1 не эскалирована владельцу — это стилистическая правка внутри уже принятого текста, не продуктовый вопрос. ## Итог High: 0. Medium: 0 — все три Medium-находки r1 закрыты предметно в `e02c282d`. Low: 1 из двух устранён (L2), один (L1, «рукоятка»/«ручка») остаётся, но снят решением ревьюера с запиской: расхождение только в описательной прозе §2/§3, не в согласованном с владельцем disabled-тексте AC2, и не влияет на реализуемость ни одного AC. Технический контракт, AC1–AC9 и границы скоупа наследуются из r1 без повторной проверки (дельта их не касается). ТЗ готово к разработке.