mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-04 13:48:57 +00:00
217 lines
19 KiB
Markdown
217 lines
19 KiB
Markdown
# SPEC-REVIEW-218-r1
|
||
|
||
- **Issue:** #218 — одна комната с floating-point шумом в координатах гасит
|
||
свечение во всём пространстве
|
||
- **Артефакт ТЗ:** `docs/specs/218-glow-floor-geometry.md`
|
||
- **Ветка/SHA:** `issue/218-glow-floor-geometry` @ `c6ff34c`
|
||
- **Трек:** обычный (не `small`) — файл ТЗ существует, что соответствует
|
||
отсутствию метки `small`/`trivial` на issue
|
||
- **Цикл:** r1/4
|
||
- **Вердикт:** жёлтый · High: 0 · Medium: 1 (в скоупе)
|
||
|
||
## Скоуп ревью
|
||
|
||
Первый цикл — разбор полный. Проверялись: тело issue #218 целиком (включая
|
||
воспроизведение владельца и мутационный гейт, вписанный туда автором отчёта),
|
||
оба комментария (аналитика владельца, хендофф автора ТЗ),
|
||
`docs/specs/218-glow-floor-geometry.md` целиком, соответствие обязательным
|
||
разделам §7.1 PROCESS.md, техническая достоверность «Подтверждённой причины»
|
||
по фактическому коду, согласованность с каноном `docs/LIGHT.md` и с
|
||
прецедентом #197 (`docs/WALL-THICKNESS.md`, `docs/ARCHITECTURE.md:414`).
|
||
|
||
## Как проверялось
|
||
|
||
- `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` — прочитаны целиком; §2.4/§7.1/§8
|
||
применены к ТЗ. Job — J1 («живая пространственная картина того, что сейчас
|
||
происходит»); анализ владельца в issue уже привязывает дефект к J1, ТЗ не
|
||
расширяет и не переопределяет привязку.
|
||
- `docs/LIGHT.md` — сверена модель Glow («лампа освещает пол, который видит»,
|
||
§«From barriers to a lit region» про `intersectionPaths`, раздел про
|
||
fail-dark источника в кладке) — контракт §8.2/§11 ТЗ ей не противоречит и
|
||
явно её сохраняет.
|
||
- `src/physical-geometry.ts:224-275` (`unionBodies`, `intersectionPaths`,
|
||
`geometryPolygonPaths`) — прочитаны построчно. Подтверждено дословно:
|
||
`unionBodies` глотает исключение polyclip и возвращает `null`
|
||
(`:228-230`); `intersectionPaths` строит `limit = unionBodies(bounds)` и при
|
||
`!limit` возвращает `[]` для всего вызова (`:263-265`), а падение самого
|
||
`intersection(base, limit)` тоже даёт `[]` с комментарием «fail dark»
|
||
(`:266-274`). Это ровно цепочка, которую ТЗ описывает в §3 — диагноз не
|
||
догадка, а прочитанный код.
|
||
- `src/physical-geometry.ts:285-302` (`floorMinusBodies`) — подтверждён
|
||
прецедент последовательного `difference` как деградации при падении
|
||
объединения; ТЗ §8.2 предлагает симметричный покомнатный путь для
|
||
`intersectionPaths`, а не изобретает шаблон с нуля.
|
||
- `src/houseplan-card.ts:14880-14998` (`_lightBarriers`) — подтверждено, что
|
||
`floor` уже является массивом полигонов **по комнате**
|
||
(`floor: polys.map((x) => x.poly)`, `:14987`), а не заранее слитой
|
||
геометрией. Это делает покомнатную деградацию из §8.2 технически
|
||
реализуемой без изменения формы входных данных, вопреки риску, что ТЗ
|
||
потребует данных, которых нет на этом уровне.
|
||
- `src/houseplan-card.ts:15001-15117` (`_renderGlowLayer`) — подтверждено, что
|
||
`intersectionPaths([seen], floor)` вызывается на каждый источник и что уже
|
||
существует guard `pointInOpaquePlanBody` для источника в кладке (fail-dark,
|
||
который §7/§8.2 ТЗ явно обязуется не ослаблять).
|
||
- `docs/WALL-THICKNESS.md:125,198,213`, `docs/ARCHITECTURE.md:414`,
|
||
`docs/TESTING.md:2304` — подтверждён прецедент #197 («один вырожденный стык
|
||
не должен гасить остальные стены всего пространства»), на который ссылается
|
||
и §4.4 ТЗ, и аналитика владельца в issue. Ссылка не декоративна — это
|
||
реальный принятый прецедент того же класса дефекта.
|
||
- `demo/smoke_glow_fail_dark.mjs`, `demo/smoke_glow.mjs`,
|
||
`demo/smoke_glow_blending.mjs`, `demo/benchmark_glow.mjs` — существование
|
||
подтверждено; это ровно файлы, которые §16.2/AC5/AC9 ТЗ называют целевыми
|
||
гейтами, а не вымышленные пути.
|
||
- `scripts/mutation-gate.mjs` — формат реестра (id/patches/guard/because)
|
||
сверен; таблица §16.3 ТЗ описывает мутанты на уровне намерения (что за
|
||
правку они бы отменили и какой guard должен упасть), а не выдаёт себя за
|
||
готовые патчи — этого от ТЗ и не требуется.
|
||
- Пересчитан по коду масштаб `quantum = 1e-6` единицы плана: `GRID_PITCH =
|
||
NORM_W/GRID_N = 1000/240`, `_cellCm` по умолчанию 5 см
|
||
(`src/houseplan-card.ts:6013-6016`) → 1 единица плана ≈ 1.2 см, то есть
|
||
`1e-6` единицы ≈ 1.2·10⁻⁸ м, а не заявленные в ТЗ «10⁻⁹ м». См. находку Low
|
||
ниже — на исполнимость AC это не влияет.
|
||
- Код продукта не менялся и не запускался на этапе `spec` — ревью
|
||
ограничивается статическим чтением `src/**` и `docs/**`; отдельно сказано в
|
||
разделе «Чего не проверял».
|
||
|
||
## Находки
|
||
|
||
### Medium (в скоупе, чинится в этом же ТЗ) — отсутствует обязательный раздел «Риски»
|
||
|
||
PROCESS.md §7.1 фиксирует список обязательных разделов ТЗ: «…AC1…ACn с
|
||
указанием доказательства · план автотестов · **риски** · откат ·
|
||
release-артефакты». В `docs/specs/218-glow-floor-geometry.md` заголовка
|
||
«Риски» нет вовсе (`grep -in "риск"` по файлу — ноль совпадений), и не только
|
||
по названию: содержательного свода «что может пойти не так» нет ни в одном
|
||
разделе. Формулировка «Quantum является техническим контрактом… изменение
|
||
после ревью требует доказать…» (§8.1) — это governance-условие на будущее
|
||
изменение константы, а не разбор рисков текущей задачи; §10 и §14 говорят про
|
||
конкретный технический контракт, но не собирают риски документа в одном
|
||
месте, как того требует чек-лист DoR (§2.5 PROCESS.md — «открытых продуктовых
|
||
вопросов нет; риски перечислены» тоже требует явного перечня).
|
||
|
||
Для сравнения — предыдущее ревью того же типа (`SPEC-REVIEW-217-r1.md`)
|
||
прямо проверяло присутствие «риски (§15)» как отдельного пункта чек-листа;
|
||
здесь пункта нет физически.
|
||
|
||
**Почему это Medium, а не High:** раздел не создаёт открытой продуктовой
|
||
неопределённости и не блокирует реализацию — содержательные предпосылки
|
||
частично рассеяны по документу (governance quantum в §8.1, недоказанность
|
||
корневой причины Glow-base в §10, стоимость прогона mutation-gate в §16.3), а
|
||
инженерная часть ТЗ технически состоятельна (проверено кодом выше). Но раздел
|
||
обязателен по §7.1 буквально, и его полное отсутствие — не опечатка, а пробел
|
||
структуры документа, который автор должен закрыть перед выходом в
|
||
«Готово к разработке».
|
||
|
||
**Что чинить:** добавить раздел «Риски», собрав минимум:
|
||
- изменение общего эпсилона `unionBodies()` затрагивает не только Glow, а
|
||
весь boolean-слой (масонри, floorMinusBodies, physicalBodiesPath) — риск
|
||
регрессии в соседних потребителях, если квантование когда-нибудь
|
||
перестанет быть no-op для валидных планов;
|
||
- наблюдение Glow-base (§10) может не воспроизвестись той же причиной — риск
|
||
остаться недокументированным при отрицательном результате;
|
||
- стоимость прогона `mutation-gate.mjs` для новых мутантов перед релизом
|
||
(уже упомянута в §16.3 как факт, но не как риск бюджета review-цикла);
|
||
- легаси-планы с перекрывающимися комнатами (упомянуты в §8.2 как то, что
|
||
нормальный путь обязан беречь) — риск того, что покомнатный fallback даст
|
||
иное покрытие пола, чем текущий merged path, на таких планах.
|
||
|
||
Это не требует переписывания ТЗ — достаточно одного раздела, синтезирующего
|
||
уже имеющийся материал; технической неопределённости к решению владельцу это
|
||
не добавляет.
|
||
|
||
### Low (снята с записью, не блокирует)
|
||
|
||
§8.1 ТЗ утверждает: «quantum `1e-6` единицы плана = 10⁻⁹ м». Пересчёт по
|
||
`GRID_PITCH = NORM_W/GRID_N` (`src/space-geometry.ts:11`, `:197-199`) и
|
||
`_cellCm` по умолчанию 5 см (`src/houseplan-card.ts:6013-6016`) даёт
|
||
`1e-6` единицы ≈ `1.2e-8` м — на порядок больше заявленного. Расхождение
|
||
чисто иллюстративное: качественный вывод абзаца («далеко за пределами любой
|
||
видимой/физической точности плана») верен при любом разумном `cell_cm`, ни
|
||
один AC и ни один тест не ссылается на конкретное число метров, и `cell_cm`
|
||
конфигурируем per-space, так что «единица = 10⁻⁹ м» в принципе не может быть
|
||
универсально точной константой. Снимаю без цикла: правки не требую, но если
|
||
автор будет трогать ТЗ по Medium-находке выше, стоит заменить точное число на
|
||
формулировку без привязки к конкретным метрам (например, «на много порядков
|
||
меньше миллиметра при любом реалистичном масштабе плана»).
|
||
|
||
## Проверка обязательных разделов §7.1
|
||
|
||
Присутствуют и содержательны: сценарий (§1) · что человек увидит до/после
|
||
(§2) · подтверждённая причина = проблема (§3) · нормативные источники и
|
||
приоритет (§4) · цели (§5) · scope/не-scope (§6/§7) · контракт поведения
|
||
(§8/§9/§10) · UX/accessibility/touch (§11) · данные/migration/privacy/i18n
|
||
(§12) · архитектура (§13) · performance/security (§14) · AC1…AC10, каждый с
|
||
доказательством (§15) · план автотестов (§16) · release-артефакты (§17) ·
|
||
откат (§18) · принятые предположения (§19).
|
||
|
||
**Отсутствует:** риски как отдельный раздел — единственный пункт списка §7.1,
|
||
не найденный в документе ни по заголовку, ни по содержанию (см. Medium выше).
|
||
|
||
Продуктовых открытых вопросов нет — и корректно нет: сценарий, ожидаемое
|
||
поведение до/после и приоритет между «не гасить весь этаж» и «сохранить
|
||
fail-dark для реально небезопасных случаев» уже зафиксированы решением
|
||
владельца в комментарии-аналитике (не додуманы автором ТЗ). Единственная
|
||
техническая неопределённость (наблюдение Glow-base, §10) явно помечена как
|
||
«если не воспроизведётся — зафиксировать и не расширять #218», что
|
||
соответствует правилу «размытое место не додумывается, а разбирается
|
||
предположением с явной пометкой» (§19 п.5 ТЗ).
|
||
|
||
Каждый AC1–AC10 однозначен, имеет названный способ доказательства
|
||
(`unit`/`browser smoke`/`golden`/«source review»/`docs diff`) и, где применимо,
|
||
явно требует падения до фикса («красный при mutant без normalization» — AC1;
|
||
«тест красный на коде до #218» — AC2) — это соответствует правилу «AC
|
||
доказывает автотест, который умеет падать», а не одно предъявление.
|
||
|
||
## Проверка «догадка выдана за факт»
|
||
|
||
Технические утверждения ТЗ о текущем поведении сверены с кодом построчно (см.
|
||
раздел «Как проверялось») и подтверждены, а не переписаны со слов автора
|
||
отчёта issue. Утверждения о том, чего ТЗ **не** гарантирует (наблюдение
|
||
Glow-base, точная конверсия quantum в метры) либо явно помечены условием
|
||
(§10), либо являются декоративной иллюстрацией без влияния на AC (см. Low).
|
||
Других мест, где предположение подано как решённый факт без пометки, не
|
||
найдено.
|
||
|
||
## Соответствие SCOPE.md
|
||
|
||
Задача устраняет полный, беззвучный отказ Core user job J1 («живая
|
||
пространственная картина происходящего сейчас») для целого пространства на
|
||
любом плане, где комната когда-либо резалась/двигалась — то есть практически
|
||
на любом реальном плане. Не расширяет продуктовый скоуп: радиус, цвет,
|
||
opacity, blend, анимация, visibility-алгоритм, проёмы и защита источника в
|
||
кладке явно исключены в §7 ТЗ и остаются неизменными. Не вводит новых
|
||
настроек, новых i18n-строк и не касается раздела «Out of scope» SCOPE.md.
|
||
|
||
## Соответствие TOUCH-SUPPORT.md / CONFIG-COMPATIBILITY.md
|
||
|
||
Изменение — чистая геометрия рендера без новых элементов UI и взаимодействий;
|
||
§11 ТЗ прямо фиксирует идентичный render для mouse/touch/pen в View/kiosk и
|
||
редакторах, что не создаёт нового touch-контракта и не требует отдельной
|
||
проверки по `TOUCH-SUPPORT.md`. §12 ТЗ фиксирует отсутствие изменений
|
||
конфига/схемы/миграции; при отсутствии новых compatibility-полей это не
|
||
конфликтует с `CONFIG-COMPATIBILITY.md`.
|
||
|
||
## Чего не проверял
|
||
|
||
- Реальная browser-геометрия и golden-рендер не запускались — на этапе `spec`
|
||
кода ещё нет; проверка ограничена статическим чтением текущего `src/**` и
|
||
сверкой заявленной цепочки вызовов с ним.
|
||
- Не проверялась реальная величина деградации/perf-профиль `benchmark_glow.mjs`
|
||
— на этой стадии нет реализации для профилирования; §14 ТЗ корректно
|
||
ограничивает решение одним линейным проходом и переносит измерение на
|
||
code-review.
|
||
- Точная форма будущего `test/physical-geometry.test.mjs` и целевого smoke
|
||
(расширение `smoke_glow.mjs` либо новый файл) не проверялась предметно —
|
||
это технический выбор автора, явно оставленный реализации (§16.2 ТЗ), и
|
||
ревью ТЗ не обязано фиксировать его заранее.
|
||
- Приватный реальный экспорт владельца не запрашивался и не читался — ТЗ
|
||
корректно ограничивается минимизированной анонимизированной fixture (§19
|
||
п.4), что и требуется privacy-контрактом §12.
|
||
|
||
## Вердикт
|
||
|
||
Жёлтый: High 0, Medium 1 — в скоупе, чинится автором в текущем ТЗ (добавить
|
||
раздел «Риски»), без создания отдельного issue. После правки — повторный
|
||
цикл разбирается по дельте (§2.9/§2.10 PROCESS.md): дельта ограничена одним
|
||
новым разделом, остальное наследуется из этого документа.
|