24 KiB
SPEC-REVIEW-193-r1
- Issue: https://github.com/Matysh/houseplan-card/issues/193
- ТЗ под ревью:
docs/specs/193-passage-placement-preview.md(коммитfc22d9a), обычный трек — неsmall/trivial - Роль: ревьюер ТЗ (не автор), этап
S4-spec-review - Трек: обычный, лимит циклов ревью ТЗ — 4 (§4 PROCESS.md)
- Цикл: r1/4
Скоуп ревью
ТЗ #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).
Как проверялось
- Прочитаны
docs/SCOPE.md,AGENTS.md,PROCESS.mdцеликом (действующая редакция, включая §1, §2.4, §2.5, §5, §7.1, §8, §10.2). - Прочитано тело issue #193 и оба комментария: аналитика владельца
(
Matysh,OWNER, оценка 6/10 · 3/10 · 3/10 · P2 ·enhancement· обычный трек, явное обоснование, почемуsmall/trivialне подходят — новый UX-контракт preview) и хендофф «ТЗ готово к ревью». - Построчно сверены все технические утверждения раздела «Подтверждённая
техническая база» (§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 ссылается на реальный файл.
- Прочитан
docs/USER-GUIDE.ru.md(разделы 1 «Термины», 9 «Двери, окна, открытые проёмы, ворота и замки»): терминология ТЗ («Открытый проём», «полупрозрачный символ», «толстая стена», ширина по умолчанию 90 см) совпадает с существующим текстом (строки 428-465), включая уже описанное поведение door/window/gate preview («Полупрозрачный символ показывает точный будущий вид... виден поверх тела толстой стены», строки 437-438) — ТЗ не изобретает интерфейсную лексику. - Прочитан
docs/WALL-THICKNESS.mdцеликом — модельhalf/толщины, growth ±½, masonry cuts согласуются с использованиемtarget.physicalHalfWidthв ТЗ; прямого противоречия канону нет. - Прочитан
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. - Проверены прямые ссылки ТЗ на файлы демо/тестов —
demo/smoke_opening_preview.mjs,demo/golden/matrix.mjs,demo/golden/harness.mjs,test/opening-symbol.test.mjs— все существуют и содержат ровно те механизмы, на которые ссылается ТЗ (см. п.3 выше). - Проверено соответствие
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-пробел, не расширяя скоуп. - Проверено, что 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действительно несёт CSSopacity: 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