mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
@@ -0,0 +1,214 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user