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

19 KiB
Raw Blame History

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.