# SPEC-REVIEW-306-r4 - **Issue:** [#306](https://github.com/Matysh/houseplan-card/issues/306) — Редактор плана: заменить виртуальные стены обычными стенами толщиной 0 - **Этап:** ревью ТЗ (PROCESS.md §2.4, повторный раунд — §2.10) - **Заход:** r4 · блокирующих циклов израсходовано (до этого вердикта) 2 из 4 - **ТЗ:** `docs/specs/306-zero-thickness-walls.md`, HEAD ветки `issue/306-zero-thickness-walls` = `3d7395abc354b072d4ed8ae48809972509dde41` («docs: align zero-wall spec limits») - **Метка issue:** `S4-spec-review` (полный трек, не `small`/`trivial`) ## Расхождение метаданных запуска с фактическим состоянием issue (процессная находка, не по ТЗ) Входные метаданные этого запуска указывали «Заход: r2 · блокирующих циклов израсходовано 1 из 4». Это не совпадает с состоянием GitHub — тот же класс расхождения, который уже фиксировали `SPEC-REVIEW-306-r2.md` и `SPEC-REVIEW-306-r3.md` для своих запусков: - r1 (`904c47e4`) — жёлтый, High 0, Medium 3 (2026-08-25T16:49:42Z) — 1 цикл; - r2 (`2c801bf5`) — зелёный, High 0, Medium 0 (2026-08-25T16:57:24Z) — цикл не тратит; - r3 (SHA на момент ревью не разрешается локально: документ r3 называет `abbd4904`, этот коммит недостижим в текущей истории — по датам и содержимому (то же сообщение «docs: adapt zero-wall spec to model v8», 2026-08-26T07:21:56+03:00) это переписанный при rebase-на-`dev` эквивалент коммита `9283d2ee`; между r2 и r3 в лог влиты посторонние коммиты `dev`, например `93054823`, что и есть след rebase) — жёлтый, High 0, Medium 1 (M1: устаревший лимит `500` вместо `MAX_WALL_SEGMENTS=200000`), 2026-08-26T07:31:54Z — 2-й цикл; - автор исправил M1 в `3d7395ab` («docs: align zero-wall spec limits», 2026-08-26T07:33:20+03:00) и передал «на финальный spec-review r4/4» (2026-08-26T07:33:36Z). Провожу разбор как **r4** по фактическому состоянию GitHub, а не как r2. Это находка процесса запуска ревью, а не находка по содержанию ТЗ #306 — отдельный issue не заводится (параметр одного прогона, не продуктовый дефект, прецедент — сам этот issue в r2/r3). ## Скоуп этого раунда Дельта между содержимым, разобранным в r3 (эквивалент `9283d2ee`), и текущим HEAD (`3d7395ab`): ``` git diff 9283d2ee..3d7395ab -- docs/specs/306-zero-thickness-walls.md ``` даёт 11 изменённых строк в трёх местах документа (§10 шаг 10, §13, §17 AC13) — ровно та правка, которую предписал вердикт r3 для закрытия M1. Правка не меняет ни одного контракта поведения, не задевает новую подсистему и не сопоставима по объёму с исходной задачей — это учебный пример локальной дельты по критерию §2.10. Разбор по дельте: заново проверено только M1 и AC13/§10/§13, чьё доказательство эта правка задевает; остальное — раздел «Унаследовано». ## Как проверялось - `git diff 9283d2ee..3d7395ab -- docs/specs/306-zero-thickness-walls.md` — единственный источник дельты, прочитан целиком построчно; - `grep -n '\b500\b' docs/specs/306-zero-thickness-walls.md` — пусто: устаревшая цифра из трёх мест M1 (§10 п.10, §13, §17 AC13) удалена полностью, замена на `` `MAX_WALL_SEGMENTS` (сейчас 200 000) `` / «любого другого актуального backend-лимита»; - `custom_components/houseplan/validation.py:1047-1048` — `MAX_WALLS = MAX_WALL_SEGMENTS = 200_000` подтверждено текущим кодом (та же строка, что и в r3, повторно проверена — правка ссылается на реальную константу, не на новую догадку); `:1056` `MAX_OPEN_SPANS = 500` — как и в r3, это предел удаляемого поля, не итогового каталога, ТЗ его не использует; - §20 «Риски и защита» (строка «Atomization превысит лимит» → «atomic failure, no truncation») уже была сформулирована без конкретной цифры — делта её не касалась, четвёртое место M1 не требовало правки; - `node --test test/docs-accept.test.mjs test/process-gate.test.mjs` — 41 passed, 0 failed, 0 skipped (Linux; автор указывал 40 passed + 1 ожидаемый Windows-skip — расхождение по площадке, не по существу); - секции 1 «Сценарий» и 2 «Что человек увидит до и после» (закрытие M1 r1) и ссылка на `docs/SCOPE.md` J4/J6 перечитаны на текущем HEAD (строки 9-26) — дельта их не касалась, текст не откатился; - `docs/SCOPE.md` — `J4`/`J6` существуют в документе (`grep -n "J4\|J6"`), утверждение §1 не голословно. ## Закрытие раунда r3 | Находка r3 | Чем закрыта | Где видно | |---|---|---| | **M1** (Medium, в скоупе) — лимит `wall_segments[]` в ТЗ назван устаревшим числом `500` в трёх местах (§10 п.10, §13, §17 AC13), противоречит уже слитому #282 (`MAX_WALL_SEGMENTS=200_000`) | Все три места переписаны на именованную константу/обобщённую формулировку без хардкода цифры | `docs/specs/306-zero-thickness-walls.md:351-353` («При превышении `MAX_WALL_SEGMENTS` (сейчас 200 000) или любого другого актуального backend-лимита отказать целиком»), `:423-424` («Лимит authoritative-каталога остаётся равен backend-константе `MAX_WALL_SEGMENTS` (сейчас 200 000)»), `:605-606` AC13 («Превышение любого актуального backend-лимита… отклоняет весь candidate без truncation/partial apply») — коммит `3d7395ab` | | Low, снято с записью r1/r3 (нет единого консолидированного блока предположений) | Не переоткрывается — r1 и r3 уже признали формат достаточным при отсутствии открытых продуктовых вопросов | без изменений в этом раунде | Других находок r3 не было (High 0, Medium 1 — только M1). ## Унаследовано из r1/r2/r3 Без повторной проверки по существу в этом раунде — дельта `9283d2ee..3d7395ab` их не касается ни текстуально, ни по содержанию: - продуктовые решения владельца по световому режиму (таблица dashed/solid, запрет фиктивной толщины, единый resolver для Glow и солнца, инвалидация кэшей при переключении, RU/EN copy под селектором) — проверены в `SPEC-REVIEW-306-r1.md`/`r2.md`/`r3.md` на SHA `904c47e4`/`2c801bf5`/ эквиваленте `9283d2ee`; §4 п.4-7, §9.1, §15 текущего файла не менялись; - соответствие `docs/SCOPE.md` (J4/J6) и отсутствие конфликта с out-of-scope — §1, §21 не в дельте; - присутствие `docs/USER-GUIDE.ru.md` в §16/§19 (закрытие M2 r1) — строки 484, 683 текущей ревизии, не в дельте; - статусы compatibility-реестра `deprecated-read`/`migrate-on-write` (закрытие M3 r1) — §6.2, строки ~172-175, проверены в r3 прямым обращением к `scripts/config-field-registry.mjs`/`docs/CONFIG-COMPATIBILITY.md`, не в дельте r3→r4; - целевая модель v8→v9 на authoritative `wall_segments[]`, stable ID/lineage #282, backend-диапазоны `cm` для `wall_segments`/`room_drafts`/`partitions`/ `wall_columns` — проверены в r3 по коду (`validation.py`), эта дельта их не меняет; - AC1, AC2, AC3, AC4, AC5, AC6, AC7, AC8, AC9, AC10, AC11, AC12, AC14, AC15, AC16, AC18 — не в дельте, наследуются из r3 без повторного разбора; - i18n-таблица §15, touch/accessibility §14 — не в дельте; - release-артефакты §19 (оба changelog, оба user-guide, канонические документы подсистем) — не в дельте. **AC13** и связанный текст §10 п.10, §13 — единственные места, разобранные заново в этом раунде (см. «Как проверялось» и таблицу закрытия выше); AC17 (перф-бюджеты) перечитан целиком заново, так как его формулировка находится в §13, но сам перф-контракт (fingerprint, отсутствие rebuild на HA tick, benchmark-пороги) не изменился словом — дельта тронула только соседнюю строку про лимит записей. ## Находки Нет. High: 0, Medium: 0. ## Что проверено и корректно - M1 закрыт по существу, не декларативно: три из трёх мест исправлены идентичной по смыслу формулировкой, привязанной к реальной именованной константе, а не к новому хардкоду; четвёртое упомянутое в r3 место (§20) не требовало правки, так как не содержало числа; - исправление не создало нового технического долга: вместо конкретной цифры документ теперь ссылается на «текущий backend-лимит» — формулировка переживёт будущее изменение константы без повторного устаревания; - docs/process gate зелёный (`node --test test/docs-accept.test.mjs test/process-gate.test.mjs` — 41/41); - секции 1-2 (§7.1), ссылка на `docs/SCOPE.md`, i18n, touch, release-артефакты не откатились и не пострадали от точечной правки — дельта минимальна и не затронула соседний текст. ## Чего не проверял - Не перепроверял AC1-AC12, AC14-AC18 по существу заново — они не в дельте r3→r4 и не зависят от лимита `wall_segments[]`; их закрытие наследуется из r3 (см. раздел «Унаследовано»); - не прогонялись `npm run typecheck`/`npm test`/`npm run build`, browser-смоки, golden, `python -m pytest tests_backend` — на этапе ревью ТЗ src не менялся ни в этой дельте, ни в дельте r3→r4; это гейты код-ревью (PROCESS.md §2.7, §8), здесь неприменимы; - не проверял заново реестр `docs/CONFIG-COMPATIBILITY.md`/ `scripts/config-field-registry.mjs` — сделано в r3, дельта их не касается; - не восстанавливал точную историю rebase (`abbd4904` → `9283d2ee`) коммит-за-коммитом — установил только, что это тот же контент по времени, сообщению коммита и последующему diff; для целей этого ревью достаточно, что дельта М1 считается от контента, который r3 реально видел. ## Вердикт M1 закрыт по существу на всех трёх упомянутых местах, новых находок дельта не создала. High: 0, Medium: 0 — **зелёный**. **Вердикт: зелёный · заход r4 · блокирующих циклов 2/4 · High: 0 · Medium: 0 → в задаче**