diff --git a/docs/reviews/SPEC-REVIEW-289-r2.md b/docs/reviews/SPEC-REVIEW-289-r2.md new file mode 100644 index 00000000..b7021efb --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-289-r2.md @@ -0,0 +1,141 @@ +# 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 без повторной проверки (дельта их не касается). ТЗ готово к +разработке.