Files
houseplan-card/docs/reviews/SPEC-REVIEW-193-r1.md
T
2026-08-19 07:44:15 +00:00

24 KiB
Raw Blame History

SPEC-REVIEW-193-r1

Скоуп ревью

ТЗ #193: инструмент «Открытый проём» в Plan editor получает специальную placement-preview геометрию только для candidate.type === 'passage' — полупрозрачный сегмент будущего разреза стены и две поперечные засечки границ, поверх уже существующих точки привязки и линеек. Сохранённый passage, door/window/gate preview, конфиг, backend, i18n, миграция и touch-контракт не меняются по тексту ТЗ.

Не в скоупе ревью: продуктовый код. Ветка issue/193-passage-preview существует и содержит один коммит (fc22d9a6c55b085dc273a70bdc8e2db22ec9fe19, Issue: #193 · User-Visible: no) — только спецификация, реализации нет. Гейты (typecheck/test/build) не прогонялись: на этапе ревью ТЗ продуктового кода не существует, прогон не относится к этому этапу (PROCESS.md §2.4/§8).

Как проверялось

  1. Прочитаны docs/SCOPE.md, AGENTS.md, PROCESS.md целиком (действующая редакция, включая §1, §2.4, §2.5, §5, §7.1, §8, §10.2).
  2. Прочитано тело issue #193 и оба комментария: аналитика владельца (Matysh, OWNER, оценка 6/10 · 3/10 · 3/10 · P2 · enhancement · обычный трек, явное обоснование, почему small/trivial не подходят — новый UX-контракт preview) и хендофф «ТЗ готово к ревью».
  3. Построчно сверены все технические утверждения раздела «Подтверждённая техническая база» (§4 ТЗ) с кодом на этой ветке:
    • _renderOpeningPlacementPreview() — src/houseplan-card.ts:17350 (заявлено 17351, расхождение на строку из-за doc-комментария, не дефект); вызывает renderOpeningVisibleGeometry(visibleSpec) без ветвления по типу — подтверждает диагноз «инструмент слеп для passage».
    • renderOpeningVisibleGeometry() (src/render/opening-symbol.ts:51-55): if (spec.type === 'passage') return svg\`;` — подтверждено дословно, включая обоснование в комментарии кода («negative space», «no jamb, leaf, arc, gate panel or standalone frame»).
    • OpeningPlacementCore (src/opening-placement.ts:33-48) содержит ровно заявленные поля: x, y, angle, renderedLength, target (OpeningPlacementTarget.physicalHalfWidth, строка 17), measure, flipH/flipV, type — ТЗ не выдумало ни одного поля.
    • openingPlacementTargets() (строки 109-140): для совпадающих segmentKey берёт Math.max(previous.physicalHalfWidth, interval.half) — подтверждает точную формулировку ТЗ «для совпадающих room-owned копий берётся максимальная реальная половина толщины».
    • _opMeasureView — src/houseplan-card.ts:11895 (заявлено 11896, тот же класс погрешности) возвращает OpMeasure, используется на 15384 — подтверждено, линейки действительно не требуют изменений.
    • Порядок SVG (src/houseplan-card.ts:15690-15733): _renderWallBodies() на строке 15723, _renderOpeningPlacementPreview() на 15727 — preview реально рисуется после тел стен и будет виден поверх кладки без нового слоя, как заявлено.
    • .opening-preview (группа, houseplan-card.ts:17366-17368) уже несёт aria-hidden="true" pointer-events="none"; .opening-preview-dot (17371-17372) — то же. Оба факта подтверждены буквально.
    • Дочерний <circle> точки использует r=${this._gridPitch * 0.18} (17372) — подтверждает §16 п.3 ТЗ: выступ засечек, предложенный тем же коэффициентом, действительно равен радиусу существующей preview-точки, а не выдуман.
    • Golden harness (demo/golden/harness.mjs:450) — allowlist ['window', 'door', 'gate'], требует .op-leaf (строка 474) — подтверждает точный диагноз «harness валидирует только типы с .op-leaf и не принимает passage».
    • Golden matrix (demo/golden/matrix.mjs:100-106) — существует ровно один сценарий opening-placement-door-thick-wall-dark (без light-варианта); ТЗ корректно называет его существующим прецедентом и не завышает его охват.
    • CSS .opening-preview { opacity: 0.5; } (src/styles.ts:1372-1375) — подтверждает реальность риска «opacity применяется дважды» (§13 ТЗ): новый сегмент рисуется внутри той же группы, и наивная реализация с opacity: 0.35 на самом rect действительно даст 0.5 × 0.35 = 0.175, как явно предупреждает ТЗ. Риск назван точно, а не гипотетически.
    • demo/smoke_opening_preview.mjs:152 — out.saveMatchesResolver уже существует под этим именем; AC2 ссылается на реальный, а не придуманный идентификатор.
    • test/opening-symbol.test.mjs существует (ls test/), AC3 ссылается на реальный файл.
  4. Прочитан docs/USER-GUIDE.ru.md (разделы 1 «Термины», 9 «Двери, окна, открытые проёмы, ворота и замки»): терминология ТЗ («Открытый проём», «полупрозрачный символ», «толстая стена», ширина по умолчанию 90 см) совпадает с существующим текстом (строки 428-465), включая уже описанное поведение door/window/gate preview («Полупрозрачный символ показывает точный будущий вид... виден поверх тела толстой стены», строки 437-438) — ТЗ не изобретает интерфейсную лексику.
  5. Прочитан docs/WALL-THICKNESS.md целиком — модель half/толщины, growth ±½, masonry cuts согласуются с использованием target.physicalHalfWidth в ТЗ; прямого противоречия канону нет.
  6. Прочитан docs/TOUCH-SUPPORT.md целиком, включая «Documentation rule» (строки 142-154): требование буквальной строки Touch editor: … для «New editor feature specifications». В тексте ТЗ #193 такой строки нет ни в одном разделе (проверено grep -n touch по файлу — единственное упоминание, строка 123 Non-scope: «расширение touch-гарантий Plan editor») — см. находку Low-1.
  7. Проверены прямые ссылки ТЗ на файлы демо/тестов — demo/smoke_opening_preview.mjs, demo/golden/matrix.mjs, demo/golden/harness.mjs, test/opening-symbol.test.mjs — все существуют и содержат ровно те механизмы, на которые ссылается ТЗ (см. п.3 выше).
  8. Проверено соответствие docs/SCOPE.md: задача — геометрия Plan editor (J4 «zero to a working plan... room polygons» / J6 «keep the plan true as the home evolves», обе персона home admin, поверхность — desktop Plan editor), View/kiosk/backend не затрагиваются; попадает в допустимый скоуп, не создаёт нового job'а. Родитель #157 (сам open passage) уже принят и закрыт как реализованный J4/J6-функционал — #193 закрывает его собственный UX-пробел, не расширяя скоуп.
  9. Проверено, что issue не помечен small/trivial: аналитика владельца явно называет причину (новый UX-контракт preview) — критерий §5 PROCESS.md «нет нового UX-контракта» не выполняется, обычный трек обоснован корректно.

Гейты (typecheck/test/build, browser smoke, golden) не прогонялись — продуктового кода нет, что и ожидается на этапе ревью ТЗ.

Обязательные разделы (§7.1 PROCESS.md)

Раздел Есть Комментарий
Сценарий (персона/поверхность/момент) ✅ §1: home admin, desktop Plan editor, наведение инструментом до клика
Что человек увидит до/после ✅ §2: одна фраза до/после, без терминов реализации
Проблема ✅ §3, подтверждена чтением кода (см. «Как проверялось» п.3)
Скоуп / не-скоуп ✅ §5/§6, скоуп ограничен геометрией+визуалом+тестами, не-скоуп явно исключает drag, символ passage, i18n, touch-расширение
Контракт поведения ✅ §7 (появление, hover/клик, после сохранения, остальные типы, события/доступность)
UX ✅ §8, таблица состояний, 7 строк
Модель данных и миграция ✅ §9, «не меняется», обоснованно для editor-only transient preview
i18n ✅ §10, «новых строк нет»
AC1…ACn с доказательством ✅ §11, 6 штук, у каждого назван способ доказательства (unit/smoke/golden/source contract)
План автотестов ✅ §12, unit/smoke/golden по отдельности, falsifiability явно потребована («на origin/dev до реализации обязаны краснеть»)
Риски ✅ §13, 8 строк риск/мера, включая подтверждённый двойной-opacity риск
Откат ✅ §14, удаление ветки/helper/стилей/тестов, без миграции
Release-артефакты ✅ §15, оба changelog, RU/EN guide, TESTING.md, dist-синхронизация, golden candidates отложены на release runbook

Присутствует также обязательный блок «Принято предположительно» (§16, 5 пунктов) — технические, не продуктовые решения, корректно отделены от продуктовых вопросов, которых, по утверждению автора и аналитики, для этой задачи нет. Проверка не нашла в §16 замаскированного продуктового вопроса: все пять пунктов (расположение чистого helper'а, имена классов, коэффициент выступа засечек, общая golden-fixture, поведение при нулевой толщине) касаются исключительно того, чего пользователь не наблюдает или что уже предрешено принципом «не выдумывать несуществующую толщину».

Находки

Low-1 — отсутствует обязательная декларация Touch editor: …

Файл: docs/specs/193-passage-placement-preview.md, весь документ (нет подходящего раздела); ближайшее место — §6 Non-scope, строка 123.

docs/TOUCH-SUPPORT.md, раздел «Documentation rule» (строки 142-154): «New editor feature specifications and code reviews must state one of: Touch editor: supported; Touch editor: best effort / intentionally degraded; Touch editor: not exposed.» ТЗ #193 добавляет новую видимую geometry в Plan editor (hover-preview) — то есть однозначно подпадает под «new editor feature specification». Полнотекстовый поиск touch по файлу даёт единственное совпадение (строка 123 Non-scope: «расширение touch-гарантий Plan editor»), которое по смыслу подразумевает ответ (не расширяем существующий best-effort статус), но не содержит требуемой буквальной строки. Тот же класс проверки в SPEC-REVIEW-192-r1 (issue #192) нашёл строку **Touch editor:** picker поддерживается как в #57 и закрыл вопрос без находки — здесь эквивалентной строки просто нет.

Обоснование severity: ответ не является продуктовой неопределённостью — Plan editor по умолчанию desktop-first/best-effort (docs/TOUCH-SUPPORT.md, таблица «Product contract»), preview — чисто presentation, hover-based эффект, на устройстве без реального hover просто не активируется тем же путём, что и сейчас для door/window/gate; отдельного решения владельца не требуется. Дефект — отсутствие обязательной для канона декларации, не дефект поведения. AC1…AC6 не зависят от этой строки.

Решение ревьюера: Low, не блокирует. Снимаю без возврата ТЗ на правку с условием: автор добавляет в ТЗ (в §6 Non-scope или отдельной строкой рядом с §7.5) явную декларацию **Touch editor:** best effort / intentionally degraded (наследует существующий статус Plan editor; preview — presentation-only hover-эффект, не расширяет touch-гарантии) до перевода issue в S5-ready — это чисто редакционное дополнение, не требующее нового цикла ревью ТЗ, и должно быть зафиксировано в хендоффе реализации или проверено на код-ревью, если автор пропустит этот шаг.

Что проверено и корректно

  • Соответствие docs/SCOPE.md: J4/J6, персона home admin, desktop Plan editor; View/kiosk/backend не затронуты; #193 закрывает собственный UX-пробел уже принятого #157, не расширяя его скоуп.
  • Легитимность обычного трека: аналитика владельца верно называет причину, по которой small/trivial не подходят (новый UX-контракт preview) — не самоощущение исполнителя, а проверяемый критерий §5 PROCESS.md.
  • Продуктовых вопросов владельцу нет и не додумано новых. Единственный потенциально продуктовый вопрос — объём видимого эффекта (сегмент + две засечки, без нового цвета/настройки) — уже зафиксирован самим текстом issue (владелец); ревью не нашло скрытой догадки, выданной за факт: каждое утверждение §4 «Подтверждённая техническая база» проверено построчно по реальному коду ветки (см. «Как проверялось» п.3) и подтвердилось без исключений.
  • Технический диагноз проблемы не голословен — renderOpeningVisibleGeometry дословно возвращает пустой SVG для passage, _renderOpeningPlacementPreview дословно не ветвится по типу; диагноз «слепой инструмент» доказан кодом.
  • Скоуп реально согласован с существующим pipeline: OpeningPlacementCore уже содержит все поля, на которые ссылается ТЗ (x, y, angle, renderedLength, target.physicalHalfWidth, measure) — реализация не потребует нового вычисления геометрии, только новую ветку рендера, как и заявлено.
  • Риск двойного opacity реален, а не гипотетичен — .opening-preview действительно несёт CSS opacity: 0.5 (styles.ts:1372); ТЗ верно определило нужную меру (отдельная стилевая область + computed-value проверка в AC6) до того, как эта ошибка попала бы в реализацию.
  • AC1–AC6 однозначны и проверяемы, каждый снабжён допустимым по §2.5 PROCESS.md способом доказательства (unit/smoke/golden/source contract); план автотестов (§12) явно требует falsifiability до реализации — «unit геометрии и smoke DOM обязаны краснеть на origin/dev».
  • Другие типы openings защищены negative-контрактом (AC4) и существующим door-golden, который явно не меняется — предотвращает случайную регрессию window/door/gate preview.
  • Существующие файлы и идентификаторы, на которые ссылается ТЗ, реальны — demo/smoke_opening_preview.mjs (saveMatchesResolver, строка 152), test/opening-symbol.test.mjs, demo/golden/harness.mjs (allowlist ['window','door','gate'], строка 450), demo/golden/matrix.mjs (opening-placement-door-thick-wall-dark, строка 100) — ни одна ссылка не выдумана.
  • Откат тривиален — preview-only ветка/helper/стили/тесты удаляются без миграции данных; #157 продолжает работать без деградации.
  • Release-артефакты ссылаются на существующие файлы (docs/CHANGELOG.md, docs/CHANGELOG.ru.md, docs/USER-GUIDE.ru.md, docs/TESTING.md) и корректно откладывают golden baseline acceptance на release runbook, а не выдают его за часть implementation loop.
  • «Принято предположительно» (§16) не маскирует продуктовый вопрос под техническое решение — все пять пунктов однозначно технические.

Чего не проверял

  • Реализацию — её нет: ветка содержит один документный коммит (fc22d9a, docs/specs/193-passage-placement-preview.md), продуктовый код (src/**) не менялся — проверено git show fc22d9a --stat.
  • Гейты typecheck/test/build/browser smoke/golden — не относятся к этапу ревью ТЗ; предмет будущего код-ревью (PROCESS.md §2.4/§8).
  • python -m pytest tests_backend — задача не затрагивает backend ни кодом, ни ТЗ.
  • Реальный визуальный результат (точные пиксельные пороги AC6, computed fill-opacity в браузере) — CSS/рендер ещё не написаны; заявленные меры (отдельная стилевая область, semantic pixel guard внутри .wallbody-fill) зафиксированы в ТЗ как план, не как исполненный факт.
  • Точность числовых оценок аналитики (ценность 6/10, сложность 3/10, P2) по существу — поле владельца (PROCESS.md §2.2), уже принятое явным решением до написания ТЗ.
  • Возможность технического спора автор/ревьюер по деталям §16 — не возникла: все пять пунктов приняты как разумные без необходимости оспаривать.

Вердикт

Зелёный. High: 0, Medium: 0, Low: 1 (отсутствует буквальная декларация Touch editor: …, обязательная по docs/TOUCH-SUPPORT.md для новых editor-фич; ответ однозначен — best effort, наследуется от существующего статуса Plan editor — снимается без возврата ТЗ на правку, с условием дописать строку до S5-ready). ТЗ подтверждено построчным чтением кода на ветке задачи: каждое техническое утверждение §4 оказалось фактом, а не догадкой; единственный потенциально продуктовый вопрос уже закрыт владельцем в теле issue; AC1–AC6 однозначны, проверяемы и снабжены допустимыми способами доказательства; риски названы точно, включая реально существующий риск двойного opacity.

Вердикт: зелёный · цикл r1/4 · High: 0 · Medium: 0 → нет · Документ: docs/reviews/SPEC-REVIEW-193-r1.md