Files
houseplan-card/docs/reviews/SPEC-REVIEW-316-r3.md
T
2026-08-26 21:54:19 +03:00

215 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC-REVIEW-316-r3
Issue: #316 · ТЗ: `docs/specs/316-opening-host-auto-resolution.md` ·
заход: r3 · блокирующих циклов израсходовано 2 из 4 (входящее значение, до
этого вердикта) · SHA материала ревью r3: `56ffbfac`
(`docs: spec #316 revision 3 — correct compatibility reference, honest
golden expectation`). Материал r2: SHA `6ca2ce8b`, документ
`docs/reviews/SPEC-REVIEW-316-r2.md`. Материал r1: SHA `36f735ae`, документ
`docs/reviews/SPEC-REVIEW-316-r1.md`.
## Скоуп раунда
Разбор — по дельте (PROCESS.md §2.9): `git diff 6ca2ce8b..HEAD --
docs/specs/316-opening-host-auto-resolution.md`. Дельта — 13 добавленных /
5 удалённых строк, целиком внутри §5.1 (ссылка на `docs/CONFIG-COMPATIBILITY.md`)
и §5.3 (утверждение про golden-сцены) плюс однострочная правка статус-строки
в шапке — это прямой точечный ответ на M3/M4 ревью r2, нормативные правила
§3, AC и границы §2 не тронуты. Оснований для полного разбора нет — дельта
меньше исходной задачи и локальна к двум абзацам.
Комментарии issue #316 проверены полностью (`gh issue view 316 --json
comments`): после вердикта r2 единственный новый комментарий — «Ревизия 3
(56ffbfac)» автора, описывающий исключительно техническую правку по M3/M4;
новых продуктовых директив владельца не появилось.
## Как проверялось
1. Найден вердикт r2 в комментариях issue (2026-08-26T18:01:35Z) и SHA
материала (`6ca2ce8b`, назван в документе r2).
2. `git diff 6ca2ce8b..HEAD -- docs/specs/316-opening-host-auto-resolution.md`
— построчная сверка дельты с текстом находок M3/M4.
3. **M3 (ссылка на `docs/CONFIG-COMPATIBILITY.md`).** Прочитан абзац
раздела «Canonical zero-thickness walls — model v9 (#306)»
(`docs/CONFIG-COMPATIBILITY.md:81-102`): цитата в новом тексте ТЗ
(«…verifies that no opening would acquire a zero host… A conflict
rejects the complete candidate») сверена посимвольно со строками 93–95
файла — совпадает дословно. Раздел «Independent-wall opening host
(#132)» (строки 188+) явно назван вне скоупа отдельным предложением —
соответствует коду (`resolveRoomOpeningHost`/`hostRoomOpenings` в
`src/wall-segment-model.ts:591/620` пропускают `host.kind==='partition'`,
что и есть предмет раздела #132). M3 закрыта корректно.
4. **M4 (claim про golden-сцены).** Новый текст §5.3 признаёт существующую
комбинацию span+проём в `coincident-partition-virtual-dark` (честно,
больше не выдаёт отсутствие за факт) и добавляет техническое
обоснование «ожидания без изменений»: «это независимая кладка вне
контурных атомов правила 3.1». Это обоснование проверено отдельно от
факта существования сцены — и оказалось неточным (см. находку M5 ниже).
Сам факт существования комбинации (закрытие исходной M4) подтверждён
повторно: `demo/golden/harness.mjs:99-112` — фикстура берётся из
`test/fixtures/276-coincident-partition.json`, `matrix.mjs:185-187` —
сцена в матрице с `mode: 'view'`, `baselines-index.json` — принята.
5. Проверка обоснования M4 по коду и фикстуре (новая находка, см. ниже):
- `test/fixtures/276-coincident-partition.json` — `rooms: [left, right]`
делят контурную границу; `walls[0]` (`cm:15`, `key` в формате
`wall_segments`) — это контурная стена между комнатами; `partitions[0]`
(`id:"redundant"`, `cm:20`) — независимая кладка, дублирующая ту же
позицию; `openings[0].host` изначально `{kind:'partition',
id:'redundant', t:0.5}` — дверь хостится на партиции, не на контуре.
- `demo/golden/harness.mjs:99-112` (`state !== 'before'`): `delete
space.partitions` — независимая кладка удаляется целиком; `delete
opening.host` — хост очищается (не остаётся `'partition'`).
- `src/wall-segment-model.ts:617-625` (`hostRoomOpenings`): пропуск
`continue` срабатывает только при `opening.host?.kind === 'partition'`
(строка 620). После `delete opening.host` условие ложно — если бы
миграция когда-нибудь прошла по этому состоянию, проём обрабатывался
бы как контурный, тем же путём `resolveRoomOpeningHost`/`segments`,
что и правило 3.1 — то есть ровно **внутри** контурных атомов, а не
«вне» них, как формулирует ТЗ.
- Реальная причина, по которой сцена не меняется, — не это (ошибочное)
утверждение, а граница §2 ТЗ: «Правила §3 действуют только при
initial migration», а голден-сцена рендерится в `mode: 'view'` без
какого-либо структурного редактирования. Подтверждено чтением
`src/houseplan-card.ts:7394-7435` — `commitWallSegmentModel`
вызывается только из пути структурного редактора (геометрический
жест), не из `setConfig`/чистого рендера; `matrix.mjs:185-187` не
содержит `dialog`/`editorTray`/иного действия, запускающего запись.
6. AC1–AC6, правила 3.1–3.4, §1, §2, §4, §6–§8 дельтой не задеты (текст не
менялся) — не перепроверялись повторно, унаследованы из r1/r2 (см.
раздел ниже).
Гейты `typecheck`/`test`/`build`/`check-docs` не прогонялись: диапазон
изменений — только `docs/specs/316-*.md`, продуктового кода нет, класс C.
Как и в r1/r2, гейтов для класса C на стадии ТЗ не предусмотрено.
## Закрытие раунда r2
| Находка r2 | Чем закрыта | Где видно |
|---|---|---|
| M3 / неверный абзац `CONFIG-COMPATIBILITY.md` | Ссылка заменена на абзац раздела «Canonical zero-thickness walls — model v9 (#306)», раздел #132 явно исключён отдельной фразой | §5.1, строки 115–122; цитата сверена дословно с `docs/CONFIG-COMPATIBILITY.md:93-95` |
| M4 / claim «в существующих сценах нет span+проём» — неточен | Формулировка заменена на признание факта: сцена существует, названа по имени, дано обоснование ожидания, назван арбитр (`golden:verify`) | §5.3, строки 136–143 — **но см. новую находку M5**: обоснование ожидания технически неточно |
M3 закрыта по существу без оговорок. M4 закрыта в части исходной претензии
(факт наличия сцены больше не отрицается), но правка внесла новое
неточное техническое утверждение — это не пропуск в закрытии M4, а новый
текст того же абзаца, добавленный при исправлении.
## Унаследовано из r2 (через r1)
Без повторной проверки приняты выводы `docs/reviews/SPEC-REVIEW-316-r2.md`
(SHA `6ca2ce8b`) и транзитивно `docs/reviews/SPEC-REVIEW-316-r1.md`
(SHA `36f735ae`) по частям документа, которые дельта r3 не затронула:
- §1 «Сценарий», §2 «Границы» — не изменились; цепочка причин, сверка с
#306 §8.2, и правило «Правила §3 действуют только при initial migration»
приняты как проверенные в r1 (и переиспользованы в r3 п.5 «Как
проверялось» для новой находки M5, но не переоткрывались).
- §3 Правила 3.1–3.4 — текст не менялся; заимствование критерия
`eligible()`, обоснование физического смысла 3.1, рендер по x/y для
безхостовых проёмов (3.3) — приняты из r1/r2 без повторного чтения кода.
- AC1–AC6 — текст не менялся, приняты как «сформулированы однозначно, со
способом доказательства» из r1, включая переписанную в r2 и подтверждённую
тогда фикстуру AC3 (стык двух контурных атомов на границе толщины).
- §5.2 (i18n), §5.4 (perf/touch), §6 «Принято предположительно», §7
«Риски», §8 «Откат» — не менялись, приняты из r2.
- Термин «непривязанный (unhosted)» — не менялся, коллизия с
`docs/UX-MODES.md` закрыта в r1/r2, повторно не проверялась.
- §5.1 — раздел не менялся целиком, кроме абзаца про
`docs/CONFIG-COMPATIBILITY.md` (перепроверен, см. выше); остальные шесть
пунктов списка файлов (закрытие M1 из r1/r2) приняты без повторного чтения.
## Находки
### M5 (Medium, в скоупе) — §5.3 обосновывает «без изменений» неточным утверждением о контурных/независимых атомах
ТЗ (строки 138–141): «`coincident-partition-virtual-dark` строит
`open_spans` поверх позиции проёма с удалённым host — но это независимая
кладка вне контурных атомов правила 3.1, поэтому ожидание — «без
изменений»».
Проверено по фикстуре и коду (детали — «Как проверялось» п.5): в состоянии
`virtual` независимая кладка (`space.partitions`, `id:"redundant"`) как раз
**удаляется** харнессом (`delete space.partitions`,
`demo/golden/harness.mjs:100`), вместе с ней очищается `opening.host`
(`delete opening.host`, строка 103). Единственная оставшаяся стена в этой
позиции — `space.walls[0]`, контурная граница между комнатами `left` и
`right` фикстуры `276-coincident-partition.json`. `hostRoomOpenings`
(`src/wall-segment-model.ts:620`) пропускает проём только при
`opening.host?.kind === 'partition'`; после очистки host это условие
ложно, значит, если бы по этому состоянию когда-нибудь прошла миграция,
проём обрабатывался бы как **контурный**, тем же путём
`resolveRoomOpeningHost`, что и правило 3.1, — то есть ровно внутри
контурных атомов, а не «вне» них, как формулирует ТЗ. Формулировка
переворачивает факт.
Практический вывод («без изменений») остаётся верным — но по другой,
уже названной в самом ТЗ причине: §2 ограничивает действие §3 initial
migration, а сцена рендерится в `mode: 'view'` (`matrix.mjs:185-187`) без
структурного редактирования — `commitWallSegmentModel` вызывается только
из пути геометрического редактора (`src/houseplan-card.ts:7394-7435`), не
из `setConfig`/чистого рендера. Значит правила §3 в этой сцене вообще не
выполняются, независимо от того, контурный это атом или нет.
**Чем грозит, если не поправить:** ошибочная формулировка учит следующего
читателя (в первую очередь код-ревью #316) неверному инварианту —
«независимая кладка защищена от правила 3.1 по природе», хотя защищена
на самом деле только *презентация без структурной записи* (§2). Если
код-ревью или реализация опираются на эту фразу как на общее правило (а
не на частный случай границы §2), это способно замаскировать реальный
случай, где такая же комбинация span+проём попадёт под структурную
запись (например, тот же конфиг после «Оптимизировать планы» или первой
структурной правки) — там рассуждение «независимая кладка» уже не
работает, потому что кладки к этому моменту в конфиге не остаётся.
**Почему Medium, а не Low:** это тот же класс ошибки, что и закрытая в
этом же раунде M3 (неверная техническая ссылка/обоснование выдана за
факт) — процесс требует помечать непроверенные технические утверждения
как предположения, а не факты; правится одной фразой без пересмотра §3
или AC.
**Как починить (без пересмотра §3):** заменить обоснование на то, что
уже верно установлено ТЗ: «сцена рендерится в `mode: 'view'` без
структурного редактирования — §3 применяется только к initial migration
(§2), а презентационный рендер её не запускает; расхождение — если оно
всё же возникнет — обязан поймать `golden:verify`».
## Что проверено и корректно
- M3 закрыта полностью и точно: цитата и номер раздела `docs/
CONFIG-COMPATIBILITY.md` теперь указывают на реально устаревающий абзац
(строки 93–95, «Canonical zero-thickness walls — model v9 (#306)»),
раздел #132 корректно исключён отдельным предложением.
- M4 закрыта в своей исходной части: ТЗ больше не отрицает существование
комбинации span+проём в golden-матрице — сцена названа явно, `golden:
verify` назван арбитром, путь приёмки при расхождении описан (не
«молчаливая переснятие»).
- Статус-строка в шапке документа корректно отражает историю раундов
(«r1: M1/M2/Low; r2: M3/M4»).
- Дельта r3 не затронула AC, §3 и границы §2 — соответствует характеру
находок r2 (обе были про формулировки в §5, не про нормативную часть).
## Чего не проверял
- Не перепроверял AC1–AC6 и правила 3.1–3.4 по коду заново — текст не
менялся дельтой r3, наследуется из r1/r2 (см. «Унаследовано из r2»).
- Не прогонял гейты typecheck/test/build/check-docs/смоки/golden — класс
C, продуктового кода ещё нет, как и в r1/r2.
- Не проверял #319 — вне скоупа, как и в предыдущих раундах.
- Не проверял остальные golden-сцены на предмет иных неточностей в §5.3
сверх найденной в M5 — одного контрпримера достаточно для находки;
исчерпывающая проверка — задача `golden:verify` на стадии кода.
- Не проверял, действительно ли `space.walls[0]` в фикстуре `276-
coincident-partition.json` при реальном прогоне миграции (а не
гипотетически) резолвится в `resolveRoomOpeningHost` без конфликта —
это не требуется для M5: находка о неточности формулировки «вне
контурных атомов», а не о существовании конфликта при миграции этой
конкретной фикстуры (которая при `mode:'view'` миграции не проходит
вовсе).
## Вывод
High-находок нет. M5 — новая находка этого раунда (текст, добавленный при
закрытии M4, а не пропуск в закрытии M3/M4 как таковом), в скоупе задачи,
чинится точечной заменой одного предложения без пересмотра §3 и AC.
Вердикт — жёлтый, документ возвращается автору на ревизию 4.