19 KiB
SPEC-REVIEW-482-r1 — Доводка экспорта пространства в PDF
- Issue: https://github.com/Matysh/houseplan-card/issues/482
- Этап: ревью ТЗ (PROCESS.md §2.4)
- ТЗ:
docs/specs/482-pdf-export-polish.md - Трек: полный (аналитика #482 назвала критерии
small, которые задача не проходит: сложность/риск 7/10, более одной поверхности, уточнение публичного контракта размеров/компоновки — обоснование присутствует) - Заход: r1 · блокирующих циклов израсходовано 0 из 4 (до этого вердикта)
Скоуп ревью
Первый заход. Разбор полный (иного и не может быть на r1): читал тело issue
#482 и все три комментария (уточнение владельца, аналитика, ссылка на ТЗ),
docs/specs/482-pdf-export-polish.md целиком, связанные контракты
docs/specs/053-pdf-export.md и docs/specs/052-view-dimensions.md
(объединён в #53), docs/SCOPE.md, docs/TOUCH-SUPPORT.md,
docs/PDF-EXPORT.md, docs/USER-GUIDE.ru.md (раздел про PDF), и — поскольку
ТЗ делает утверждения о текущем поведении кода как обоснование problem
statement — сверил каждое такое утверждение с фактическим src/pdf/*.ts,
src/near-axis.ts, src/wall-thickness.ts, src/styles/plan.styles.ts.
Как проверялось
- Продуктовая рамка:
docs/SCOPE.md— задача внутри узкого исключения печатного экспорта (зафиксировано 2026-08-15/2026-09-07 для #53/#52), сама ничего не расширяет (§4 ТЗ явно ограничивает изменяемый контракт частями #53, §6 явно перечисляет не-скоуп). - Процесс:
AGENTS.md,PROCESS.md§1–§9 — трек, статусы, лимит циклов, требования DoR (§2.5), обязательные разделы ТЗ (§7.1). - Само ТЗ построчно на однозначность, проверяемость каждого AC и наличие способа доказательства.
- Отдельно — не выдаёт ли автор догадку за факт. Для этого каждое
утверждение раздела «Подтверждённые причины» (§3 ТЗ) и технической карты
(§20) сверено с реальным кодом:
compactRing()(src/pdf/pdf-dimensions.ts:16-33) действительно ищет коллинеарные точки поprevious/point/nextдо какого-либо схлопывания точных/почти точных дублей — корневая причина ложной хорды подтверждена чтением, не предположением;WALL = [0.33, 0.33, 0.33](pdf-scene.ts:79) — заливка без штриховки, подтверждает «нет hatch-прохода»;pdf.legend.*(6 ключей: wall/partition/virtual/door/window/gate) вpdf-scene.ts:522-533иsrc/i18n/en.json:454-459— легенда и ключи существуют ровно как описано;- выбор ориентации
landscape = physicalWidth > physicalHeight(pdf-scene.ts:236) и резервыcalloutWidthMm = 48,dimensionReserveMm = 30(pdf-scene.ts:246,250) — подтверждают «сырой bbox до размеров» и «приближённые 30/48 mm» из §3.4/§12.1 ТЗ; - цветовой форматтер
fmt = (v) => v.toFixed(2)...(pdf-writer.ts:38) — общий двухзнаковый форматтер действительно даст0.5для127/255, подтверждает необходимость отдельного high-precision форматтера (§11, риск-таблица); - в
pdf-writer.tsнет ни одногоW/W*clip-оператора — подтверждает «writer не умеет even-odd clipping» (§3.5); - канонический допуск
NEAR_AXIS_MAX_DEGREES = 0.25реально существует вsrc/near-axis.tsи используется вне PDF (repair/draw) — ссылка ТЗ §8.2 на «канонические 0.25°» не изобретена, это существующая общая константа; - fallback «выноска R для непрямоугольной комнаты» (§10.2 ТЗ) —
реальный код
pdf-scene.ts:422-479(nonRect,callouts,R${…}), не придуманный механизм; - существующий экранный hatch (
houseplan-card.ts:9259-9263,space-render.ts:902-906) действительно повёрнут на 45° — обоснование «как на самом плане» для §11 ТЗ не голословно.
docs/PDF-EXPORT.mdподтверждает, что легенда и порядок футера (масштаб, scale bar, north, дата, версия) — уже документированное поведение, которое §14 ТЗ корректно урезает, не придумывая нового.docs/TOUCH-SUPPORT.md— диалог PDF относится к View и подпадает под «полностью поддерживается», это верно отражено в §16 ТЗ (320 px, а не best-effort).
Гейты кода на этом этапе не запускались — ревью ТЗ оценивает выполнимость и
проверяемость постановки, а не код; кода по этому issue ещё нет (S4-spec-review,
ветка issue/482-pdf-export-polish содержит только ТЗ и предыдущие
несвязанные коммиты beta.3).
Находки
Medium (в скоупе задачи)
M1 — источник и лицензия векторного компаса заявлены как факт без
проверяемой цитаты, аналогично истории #159. docs/specs/482-pdf-export-polish.md
§13: ТЗ утверждает, что приложенный владельцем compass-svgrepo-com.svg
(так его в issue называет владелец) — это «compass-line из VMware Clarity
Assets», MIT license, copyright VMware 2018, ссылается на конкретный upstream
URL и приводит SHA-256 «переданного владельцем файла», который «совпадает по
path с каноническим ассетом».
Ни владелец в issue, ни один документ репозитория (docs/FURNITURE.md,
docs/PDF-EXPORT.md, лицензионные заметки) не называют этот источник —
владелец лишь приложил геометрию SVG и попросил заменить стрелку. Атрибуцию
«VMware Clarity Assets, MIT, © 2018» вносит сам автор ТЗ. Это ровно тот
класс дефекта, который PROCESS.md требует ловить на этапе ревью ТЗ: «догадка,
записанная как факт» — и ровно тот тип дефекта, что уже случился на #159
(SPEC-REVIEW-159-r1.md, High-1: провенанс/лицензия SVG опирались на
комментарий, который не подтверждал ни авторства, ни лицензии).
В этом ревью у меня нет доступа к внешним URL (инструмент веб-фетча не
подтверждён средой — см. «Чего не проверял»), поэтому я не могу ни
подтвердить, ни опровергнуть конкретное совпадение path/SHA-256. Именно
поэтому фактическая проверка не может остаться на совести одного только
автора: пока в самом ТЗ нет воспроизводимого способа перепроверить
атрибуцию (например, точный upstream commit/blob вместо общей ссылки на
master, который может уехать, или явное подтверждение владельца, что он
взял файл из названного источника), AC8 «license сохранена» проверяется
только по тому, что реализация совпадает с недоказанным утверждением ТЗ, а
не с внешней истиной.
Почему в скоупе, а не отдельным issue: это ровно тот же деливерабл, что и пункт 7 issue/§13 ТЗ (векторный компас), не соседняя подсистема — чинится правкой этого же ТЗ, не новым issue (#202).
Как закрыть (на выбор автора, ТЗ не подсказывает решение):
- заменить общую ссылку на
masterточным upstream commit SHA/blob, который не уедет, и явно пометить абзац как «принято предположительно, проверить перед реализацией» по механике §7.1 PROCESS.md; или - получить у владельца прямое подтверждение источника одним продуктовым вопросом («откуда взят приложенный SVG-файл — просто подтвердите источник/лицензию, чтобы не приписать чужой копирайт неверно»); или
- обойтись без внешней атрибуции вовсе: сохранить в репозитории только то, что доказуемо — сам SHA-256 приложенного владельцем файла как idempotency proof, без утверждений о конкретном третьесторонним репозитории/лицензии, которые ТЗ не может подтвердить.
Без High-находок это жёлтый вердикт: правка ТЗ и повторный заход в рамках той же задачи, отдельный issue не заводится.
Что проверено и корректно
- Продуктовая рамка (§1–§2 ТЗ): персона, поверхность и момент названы верно; предложение «до/после» одной фразой без терминов реализации выдержано.
- Скоуп/не-скоуп (§5–§6): граница задачи явная, включая явный триггер
возврата в
S3-specпри выходе за неё (новое config-поле, диагональные стены, новые галочки). - Изменяемый контракт (§4): точно называет, какие части #53/#52 заменяются, а какие гарантии сохраняются — не тихая ревизия чужого ТЗ.
- Нормализация контуров (§8): порядок очистки (схлопнуть дубли → удалить коллинеарные) и допуски (1 mm физических, 0.25° из существующей константы) однозначны и проверяемы; описание «промежуточной точки» через ненулевые векторы и положительное скалярное произведение исключает как раз тот дефект, что породил issue.
- Локальная дедупликация (§9): критерии пары (ось, нормаль, interval, длина, inside-probe) и правило разрешения конфликтов (score → стабильный ключ) полностью детерминированы, включая явные «не следствия» (разные комнаты/rings/оси не трогать).
- Размещение размеров (§10): единая полоса, сдвиг целиком, шаги в мм — корректно запрещает старый баг «tangent-jitter отдельного текста».
- Материал стен (§11) и компоновка листа (§12): точные числа (127/127/127, 45°/3 mm/0.18 mm, допуск 0.5 mm на bbox, порядок выбора масштаба) — каждое либо взято из уже существующей константы (45°, INK), либо является явным новым техническим решением автора, не заявленным как продуктовое наблюдение владельца — то есть законная зона «решает автор».
- Footer/i18n (§14): корректно ссылается на реально существующие
pdf.legend.*ключи и текущий состав footer изdocs/PDF-EXPORT.md. - Модель/миграция/rollback (§15), touch/a11y/perf/security (§16): все пункты DoR присутствуют; миграции нет, что верно для чисто визуальной доводки.
- AC1–AC12 (§17): каждый пронумерован, привязан к разделу контракта и несёт способ доказательства (unit/smoke/golden/mutation witness), включая защитные критерии (дедупликация, axis filter, clipping) — это избыточно подробно относительно минимума DoR (§2.5), что для риска 7/10 уместно.
- Тест-план (§18) и границы Windows/Linux для golden корректно повторяют
канон
PROCESS.md§8 (golden:verifyдиагностический на Windows, канонические эталоны — только Linux CI). - Документация и release-artifacts (§19): перечислены все реально
существующие затрагиваемые документы, включая замену контракта в
docs/specs/053-pdf-export.md. - Карта изменений (§20) соответствует существующей структуре
src/pdf/*— файлы, которые ТЗ называет изменяемыми, действительно существуют (pdf-dimensions.ts,pdf-scene.ts,pdf-writer.ts,hp-pdf-dialog.ts);pdf-compass.ts— новый файл, что согласовано явно. - Открытых продуктовых вопросов к владельцу нет и не требуется: все технические развилки (где хранить lane-состояние, как разрешать коллизии) решены и явно объявлены зоной ответственности автора/ревьюера (§7.1 PROCESS.md), а не вынесены как недостающие продуктовые решения.
Чего не проверял
- Не запускал
npx tsc --noEmit/npm test/npm run build— на этапе ревью ТЗ кода по задаче ещё нет (ветка содержит только сам файл ТЗ поверхv1.73.0-beta.3), гонять гейты не по чему. - Не проверял независимо через внешний URL, действительно ли приложенный
владельцем SVG побайтово совпадает с
vmware-archive/clarity-assetsи действительно ли лицензия этого репозитория — MIT с указанным копирайтом: инструмент веб-фетча запросил разрешение и не получил его в этой сессии. Это прямая причина находки M1, а не молчаливый пропуск. - Не проверял смоки/golden/perf — на стадии ТЗ им нечего мерить; тест-план ТЗ (§18) сам называет их как будущее доказательство AC, что и является предметом код-ревью, а не этого захода.
- Не проверял
docs/specs/README.md/docs/STATUS.md/docs/ARCHITECTURE.mdпострочно на предмет того, что именно там устареет — ТЗ (§19) верно называет их условно («при изменении перечисленных модулей»), это станет предметом код-ревью, когда будет виден фактический diff.
Вердикт
Одна находка Medium в скоупе задачи, High нет. По PROCESS.md §2.4/§4: без High-находок это жёлтый вердикт — ТЗ возвращается автору на правку в рамках этого же issue, отдельный issue не заводится (#202). Бюджет циклов ревью ТЗ для полного трека — 4; этот заход тратит один цикл (жёлтый вердикт бюджет расходует, в отличие от зелёного, #227).
Материал раунда
- Ветка:
issue/482-pdf-export-polish, коммитbc93babdf8e7— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
38bf116ad9ad52b85ed8e0aaee3799819a5a1e58git log --all --format='%H %T' | grep 38bf116ad9ad - ТЗ
docs/specs/482-pdf-export-polish.md, блоб28628a6c925a3a20886cf0315499ffd478ea8a25git log --all --find-object=28628a6c925a3a20886cf0315499ffd478ea8a25 -- docs/specs/482-pdf-export-polish.md