Files
houseplan-card/docs/reviews/SPEC-REVIEW-482-r1.md
T
2026-09-07 06:15:34 +00:00

216 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.
## Как проверялось
1. Продуктовая рамка: `docs/SCOPE.md` — задача внутри узкого исключения
печатного экспорта (зафиксировано 2026-08-15/2026-09-07 для #53/#52), сама
ничего не расширяет (§4 ТЗ явно ограничивает изменяемый контракт частями
#53, §6 явно перечисляет не-скоуп).
2. Процесс: `AGENTS.md`, `PROCESS.md` §1–§9 — трек, статусы, лимит циклов,
требования DoR (§2.5), обязательные разделы ТЗ (§7.1).
3. Само ТЗ построчно на однозначность, проверяемость каждого AC и наличие
способа доказательства.
4. Отдельно — не выдаёт ли автор догадку за факт. Для этого каждое
утверждение раздела «Подтверждённые причины» (§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<n> для непрямоугольной комнаты» (§10.2 ТЗ) —
реальный код `pdf-scene.ts:422-479` (`nonRect`, `callouts`, `R${…}`), не
придуманный механизм;
- существующий экранный hatch (`houseplan-card.ts:9259-9263`,
`space-render.ts:902-906`) действительно повёрнут на 45° —
обоснование «как на самом плане» для §11 ТЗ не голословно.
5. `docs/PDF-EXPORT.md` подтверждает, что легенда и порядок футера (масштаб,
scale bar, north, дата, версия) — уже документированное поведение, которое
§14 ТЗ корректно урезает, не придумывая нового.
6. `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).
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/482-pdf-export-polish`, коммит `bc93babdf8e7` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `38bf116ad9ad52b85ed8e0aaee3799819a5a1e58`
```
git log --all --format='%H %T' | grep 38bf116ad9ad
```
- ТЗ `docs/specs/482-pdf-export-polish.md`, блоб `28628a6c925a3a20886cf0315499ffd478ea8a25`
```
git log --all --find-object=28628a6c925a3a20886cf0315499ffd478ea8a25 -- docs/specs/482-pdf-export-polish.md
```