# SPEC-REVIEW — issue #449 · заход r3 (см. «Расхождение нумерации» ниже) - Issue: https://github.com/Matysh/houseplan-card/issues/449 - Этап: ревью ТЗ (PROCESS.md §2.4) - ТЗ: `docs/specs/449-double-fit-all.md` - Материал: ветка `issue/449-double-fit-all`, SHA `9238ce666bee7ea9b7eddcd9e5b3f083b96ad114` - Трек: полный (issue не помечен `small`); лимит циклов ревью ТЗ — 4 (§2.4) - Заход r3 (истинный порядковый номер — см. ниже), блокирующих циклов израсходовано до этого вердикта: **2**/4 (r1 — жёлтый, r2 — жёлтый) ## Расхождение нумерации: env дал «заход r2 · циклов 1/4», это неверно Заголовок задачи для этого прогона (`needs.guard.outputs.cycle`/`spent`, рендерится в `.github/workflows/process.yml:437-440,625`) сообщил «Заход: r2 · блокирующих циклов израсходовано 1 из 4». Это не совпадает с историей issue: - r1 — жёлтый вердикт, коммент [#issuecomment-5542152824](https://github.com/Matysh/houseplan-card/issues/449#issuecomment-5542152824), документ опубликован коммитом `9206ef64` под именем `docs/reviews/SPEC-REVIEW-449-r1.md`; - правка `e027b2a0` закрыла M1; - r2 — жёлтый вердикт, коммент [#issuecomment-5542290451](https://github.com/Matysh/houseplan-card/issues/449#issuecomment-5542290451), сам документ внутри себя корректно называет себя «заход r2» и явно ссылается на материал r1 (`9206ef64`, SHA `5847bf2b1bb6`); - правка `9238ce66` (материал этого раунда) закрывает M2. То есть до этого прогона фактически было **два** жёлтых вердикта, а не один, и этот прогон — **третий**, а не второй. **Корневая причина найдена.** `guard`-джоб считает `attempt`/`spent` не по номеру, а по числу комментариев issue, которые одновременно проходят два regex-теста: `Вердикт:` и буквальную подстроку маркера (`SPEC-REVIEW` для этапа spec) — `.github/workflows/process.yml:87-93`. Комментарий r1 закончился фразой «Документ: см. артефакт ревью (публикуется шагом конвейера)» — **без имени файла**, поэтому подстроки `SPEC-REVIEW` в его теле нет, и jq-фильтр `of_stage` его не засчитывает. Комментарий r2, наоборот, заканчивается «Документ: `docs/reviews/SPEC-REVIEW-449-r2.md`» — с маркером — и он единственный учтён. Итог на момент этого прогона: `of_stage` = 1 (только r2), `attempt` = 1+1 = 2, `spent` = 1. Оба числа занижены ровно на единицу — комментарий r1 исключён из подсчёта навсегда, если его текст не поправить. **Уже нанесённый ущерб.** Тем же образом на предыдущем прогоне `guard` посчитал `cycle=1` для того, что публикующийся ревьюер верно назвал «заход r2» в тексте. Шаг публикации (`.github/workflows/process.yml:661`, `doc="docs/reviews/${marker}-${NUM}-r${CYCLE}.md"`) берёт номер не из текста ревью, а из `$CYCLE` — и положил документ r2 в файл `…-r1.md`, **затерев** оригинальный документ r1. Оригинал не потерян безвозвратно — он читается из истории git: ``` git show 9206ef64:docs/reviews/SPEC-REVIEW-449-r1.md ``` — но рабочее дерево на `origin/dev` сейчас содержит под именем `…-r1.md` содержимое второго раунда, а файла `…-r2.md` не существует вовсе. **Что это значит для этого прогона.** Публикующий шаг снова возьмёт номер из `$CYCLE` (=2, по тому же занижению) и положит **этот** документ в `docs/reviews/SPEC-REVIEW-449-r2.md`. Это не перезапишет существующий файл (такого файла ещё нет), поэтому данные в этот раз не теряются — но нумерация файла (`r2`) разойдётся с истинным порядковым номером раунда (третий), и то же занижение на единицу останется навсегда, пока кто-то не поправит текст комментария r1 (добавив в него подстроку `SPEC-REVIEW-449-r1`) либо саму логику подсчёта в `guard`. Ни то, ни другое не лечится в ветке `issue/449-double-fit-all` и не входит в скоуп ТЗ #449, поэтому заведён отдельный issue — [#454](https://github.com/Matysh/houseplan-card/issues/454) — со ссылкой на этот документ и точной репродукцией. Числа `spent`/`limit`, которые увидит владелец в шапке будущих прогонов, тоже занижены на единицу: лимит `review-4` фактически сработает на пятом жёлтом вердикте вместо четвёртого. На этом issue лимит не выбирается ни при буквальном, ни при истинном счёте (2 или 3 из 4), поэтому решение по вердикту этого раунда не меняется — расхождение зафиксировано для владельца и починки инфраструктуры, а не потому, что оно блокирует #449. Дальше в этом документе использую **истинный** счёт (r3, циклов 2 уже потрачено до этого раунда), а не буквальные числа из шапки задачи — иначе номер в тексте противоречил бы фактической истории раунда, которую сам же документ и восстанавливает. Это решение не влияет на будущий автоматический подсчёт: `guard` считает по наличию подстрок `Вердикт:`/`SPEC-REVIEW` в теле комментария, а не по значению, которое написано после «заход r». ## Скоуп проверки — по дельте (PROCESS.md §2.10) Предыдущий раунд (r2 по факту истории, физически опубликован как документ `docs/reviews/SPEC-REVIEW-449-r1.md` из-за описанного выше искажения нумерации) получен на материале SHA `e027b2a06d62b79530840a36369651d5eea38839`. Правка — один коммит `9238ce66` поверх него, ребейза не было (`git merge-base origin/dev 9238ce66` = `89647c10`, та же база, что и у r1/r2). ``` git diff e027b2a0..9238ce66 -- docs/specs/449-double-fit-all.md ``` ```diff - **Touch editor: not exposed** — жест живёт только во View и kiosk; редакторы - Плана, Устройств, Декора и Подложки его не получают, их touch-поведение и - редакторский double-click не меняются (§6 «Режимы», AC7). Правило + **Touch editor: not exposed** — жест живёт только во View и kiosk; редакторы + Плана, Устройств и Подложки (в ней редактируется декор) его не получают, их + touch-поведение и редакторский double-click не меняются (§6 «Режимы», AC7). `docs/TOUCH-SUPPORT.md` → «Documentation rule». ``` 3 строки, ровно та формулировка, которую предыдущий раунд предложил как исправление M2. `docs/specs/README.md` не менялся. `git diff origin/dev...HEAD` содержит только `docs/reviews/SPEC-REVIEW-449-r1.md` (артефакт прошлого раунда), `docs/specs/449-double-fit-all.md`, `docs/specs/README.md` — класс C целиком, продуктовый код по-прежнему не тронут. Дельта локальна: не ребейз, не смена контракта, не новая подсистема, объём — 3 строки против находки в одну неточность. Разбор ограничен дельтой плюс всем, до чего она дотягивается: сама строка и разделы, на которые она ссылается (§6, AC7, канонический словарь редакторов). Остальные разделы ТЗ дельта не задевает — см. «Унаследовано». ## Как проверялось 1. Получены оба вердикта (`gh issue view 449 --json comments`) и раскрыт их реальный порядок и содержание — расхождение с шапкой задачи описано выше. 2. `git diff e027b2a0..9238ce66 -- docs/specs/449-double-fit-all.md` — ровно правка из предложения r2, больше в файле ничего не менялось. 3. Независимо пересчитан состав редакторов по тем же трём источникам, что называло r2, плюс дополнительно код: - `docs/USER-GUIDE.ru.md:201-206` — таблица режимов называет три редактора: План / Устройства / Подложка; - `docs/UX-MODES.md:186` — `## Background — the decor underlay`, декор — содержимое Подложки; - `scripts/check-docs.mjs:115` — `[/\bDecor editor\b/gi, 'Background editor']` нормализует устаревшее имя того же редактора; - `grep -n -i "декор\|редактор" docs/specs/449-double-fit-all.md` — во всём файле (шапка, «Не-скоуп» :73, AC7 :279-286) теперь ровно три редактора, без расхождений. 4. Ссылки новой строки на §6 (`:180-188`) и AC7 (`:279-286`) открыты и сверены построчно — оба раздела называют те же три редактора, что и исправленная шапка. 5. `node scripts/check-docs.mjs` — прогнан лично, `Documentation checks passed (7 files, 12 external links)`, exit 0. 6. `git diff --check e027b2a0..9238ce66 -- docs/specs/449-double-fit-all.md` — чисто, exit 0. 7. Обязательные разделы §7.1 пересчитаны по текущему файлу (`grep -n "^## " docs/specs/449-double-fit-all.md`) — все 15 присутствуют, структура не пострадала от правки одной строки. 8. `docs/specs/README.md:178` — запись issue↔ТЗ на месте, дельтой не тронута. 9. Трейлеры `9238ce66` (`git show -s --format=full`) — `Issue: #449`, `User-Visible: no`, верно для документационной правки без изменения поведения. ## Закрытие раунда r2 (по факту истории; физически — документ `…-r1.md`) | Находка r2 | Чем закрыта | Где это видно | |---|---|---| | **M2** (Medium, в скоупе): строка `Touch editor: not exposed` называла несуществующий четвёртый редактор («Декора» отдельно от «Подложки») | Формулировка заменена на «Плана, Устройств и Подложки (в ней редактируется декор)» — те же три редактора, что в §6/AC7/гайде | `docs/specs/449-double-fit-all.md:9-12` (коммит `9238ce66`) | Проверено не на слово автора: цитаты трёх источников (`USER-GUIDE.ru.md`, `UX-MODES.md`, `check-docs.mjs`) сверены заново лично (см. «Как проверялось» п.3), а не приняты по формулировке коммента. Новая строка теперь дословно согласована с §6 и AC7, на которые сама же ссылается — расхождения, которое породило M2, больше нет нигде в файле. ## Находки Нет находок уровня High/Medium/Low в дельте r3 по содержанию ТЗ #449. Отдельно (не находка против ТЗ, а находка против инфраструктуры ревью) — см. раздел «Расхождение нумерации» выше и заведённый [#454](https://github.com/Matysh/houseplan-card/issues/454). Не учитывается в счётчике High/Medium этого вердикта: предмет ревью — ТЗ #449, а не пайплайн. ## Что проверено и корректно - **M2 закрыта по существу**, а не только по форме: три независимые сверки (USER-GUIDE.ru.md, UX-MODES.md, check-docs.mjs) плюс перечитанные §6/AC7 подтверждают, что шапка теперь называет ровно тех же трёх редакторов, что и остальной документ. - **Никакого нового текста, кроме согласованной формулировки, правка не вносит** — дифф ограничен тремя строками ровно там, где была неточность. - **`node scripts/check-docs.mjs` зелёный** (прогнан лично на этом SHA). - **`git diff --check` чист** — не внесено пробельных дефектов. - **Обязательные разделы §7.1 на месте**, `docs/specs/README.md` не требовал правки и не тронут. - **Трейлеры `9238ce66` корректны**: `Issue: #449`, `User-Visible: no`. - **Дельта действительно локальна** — не ребейз (`merge-base` не изменился с r1/r2), не новая подсистема, не смена контракта: полный повторный разбор не требовался. ## Унаследовано из r1 и r2 Всё, что предыдущие раунды проверили и признали корректным, а дельта r3 (3 строки правки M2) не задевает, принято без повторной проверки: - продуктовая рамка — job J1, персоны Household/Guest (View) и Home admin (kiosk), задача в скоупе `docs/SCOPE.md`; проверено в r1 (`docs/reviews/SPEC-REVIEW-449-r1.md`@`9206ef64`, доступен по этому SHA в истории git — рабочая копия того же пути сейчас содержит текст r2, см. «Расхождение нумерации»); - решения владельца Q1/Q2 корректно перенесены в контракт поведения (§5) и таблицу §6; сама таблица §6 не менялась ни в r2, ни в r3 — построчно перечитана в этом раунде заново только по факту ссылки новой строки на неё (см. «Как проверялось» п.4), не как повторная приёмка всего раздела; - построчная сверка «текущего поведения по коду» (`_lastTap`/`_swipeStart` внутри kiosk-ветки, отсутствие `dblclick` на stage, сигнатура `_fitAll`, `ROOM_FIT_INTERACTIVE_OWNER`, `CameraTransitionReason`, no-op на `sameCameraState`) — код не менялся ни разу за все три раунда, повторная сверка не требуется; - существование всех файлов, названных в «Плане автотестов» и AC1–AC11 (smoke/test/mutation-gate) — файлы не переименовывались и не удалялись; - обязательные разделы §7.1 присутствуют все, AC пронумерованы и каждый несёт способ доказательства — подтверждено в r1, пересчитано механически заново в этом раунде (п.7 «Как проверялось»), без повторного смыслового чтения каждого AC; - блок «Принятые предположения» отделяет техническое от продуктового корректно — не менялся с r1; - вне-скоуп находка «Показать всё» / «Вписать всё» в `docs/USER-GUIDE.ru.md` верно заведена r1 как [#452](https://github.com/Matysh/houseplan-card/issues/452) и не относится к предмету этого раунда. ## Чего не проверял и почему - **Реализацию** — её по-прежнему нет: `git diff origin/dev...HEAD` не содержит класса A/B, только `docs/**`. - **`npx tsc --noEmit` / `npm test` / `npm run build`** — не гонял: раунд не меняет ни одной строки кода, а Validate на предыдущем продуктовом SHA к этому ТЗ не относится (ТЗ ещё не имеет реализации). - **Инварианты модели / golden / браузерные smoke** — не применимо: дифф не трогает `src/**`, геометрию, рендер. - **Полный текст ТЗ вне зоны дельты** (сценарий, скоуп/не-скоуп кроме списка редакторов, модель данных, риски, план автотестов, откат, release-артефакты) — не перечитывал заново; ничто из этого не зависит от трёх изменённых строк, см. «Унаследовано». - **Q1/Q2 продуктовую историю** — не переоткрывал: решения владельца зафиксированы до r1, дельта их не касается. - **Историческую правку комментария r1** (добавление в него маркера `SPEC-REVIEW-449-r1`, чтобы восстановить корректный счёт `guard`) — не делал: это чужой опубликованный комментарий, редактировать его не входит в роль ревьюера; чинить нужно логику подсчёта, а не задним числом переписывать историю issue. Отражено в заведённом [#454](https://github.com/Matysh/houseplan-card/issues/454). ## Вердикт High: 0 · Medium в скоупе ТЗ #449: 0 · Medium вне скоупа: 0 (обе прежние вне-скоуп находки уже закрыты решением r1: #452 заведена, эта находка выше — о пайплайне, а не о соседнем продуктовом поведении, заведена отдельно как #454 в силу серьёзности, но не считается в этом тэлли, так как её предмет — не ТЗ). Зелёный вердикт (PROCESS.md §2.4, §12): M2 закрыта по существу, новых находок против содержания ТЗ #449 нет. Зелёный вердикт бюджет циклов не тратит — истинный счёт остаётся 2/4 (r1, r2), а не увеличивается этим раундом. ТЗ #449 готово к `S5-ready`. --- ## Материал раунда - Ветка: `issue/449-double-fit-all`, коммит `9238ce666bee` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `ee0b2f4528fb30de7b90d79323c7aa16c96af7e7` ``` git log --all --format='%H %T' | grep ee0b2f4528fb ``` - ТЗ `docs/specs/449-double-fit-all.md`, блоб `d6ddccbb3920bdffbb8414f4b6c225a4cdf4f420` ``` git log --all --find-object=d6ddccbb3920bdffbb8414f4b6c225a4cdf4f420 -- docs/specs/449-double-fit-all.md ```