Files
houseplan-card/docs/reviews/SPEC-REVIEW-530-r2.md
T
2026-09-11 09:54:24 +00:00

236 lines
20 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-530-r2
**Issue:** #530 — «Экспорт в PDF: план занимает половину листа A4, а столбец выносов отбирает у него целый шаг масштаба»
**Этап:** spec (ревью ТЗ, PROCESS.md §2.4)
**Трек:** полный (см. r1 — обоснование не изменилось)
**Заход:** r2 · блокирующих циклов израсходовано 1 из 4 (зелёный вердикт этого раунда бюджет не тратит, #227)
## Скоуп раунда
Разбор по дельте (PROCESS.md §2.10): предмет этого раунда — правка тела issue,
которую автор внёс в ответ на SPEC-REVIEW-530-r1.md (вердикт: жёлтый, Medium 1,
Low 1). Раунд НЕ переоткрывает продуктовую рамку, AC1–AC6 и техническое
обоснование полного трека — они не входят в дельту и наследуются из r1.
## Как найдена дельта
Тело issue правится на месте (ТЗ живёт в теле issue, #517), поэтому вместо
`git diff <SHA>` использован диф двух состояний тела issue:
1. якорь материала r1 в конце `docs/reviews/SPEC-REVIEW-530-r1.md`: `Тело issue:
3c6248e088a3948ba1b6a44fafbc15118b8ad7706edb941b92b3fd9a4c8135ac`;
2. история правок тела issue вытянута через GraphQL
(`Repository.issue.userContentEdits`, поле `diff` = полный снимок тела на
момент каждой правки, 5 снимков, автор — Matysh, последняя правка
`2026-09-11T09:48:17Z`);
3. `sha256sum` снимка `2026-09-11T09:37:32Z` (правка, сделанная непосредственно
перед комментарием «ТЗ готово — на ревью», `09:37:50Z`) —
`3c6248e088a3948ba1b6a44fafbc15118b8ad7706edb941b92b3fd9a4c8135ac` — совпал
побайтово с якорем r1. Это доказывает, что снимок и есть точный текст,
получивший вердикт r1, а не приближение;
4. `diff -u` этого снимка с текущим телом issue (снимок `09:48:17Z`, совпадает
с текущим телом issue с точностью до конечного перевода строки) дал
дельту раунда — приведена целиком ниже.
```diff
@@ раздел «ТЗ», шапка @@
-**Чего задача не трогает:** i18n, кроме удаления ключа `pdf.internal_dimensions`; ...
+**Документация:** правка меняет канонический `docs/PDF-EXPORT.md` — раздел
+«Sheet and measurement rules» описывает и выноски («required values that
+cannot fit beside an edge use numbered callouts», «architecture, enabled
+dimensions and callouts»), и правило отказа («If a fixed annotation itself
+cannot fit on one A4 sheet, export stops with an error»), единственный
+код-путь к которому — именно столбец. Документ правится тем же коммитом,
+формулировки — в п. 1 и 5 контракта.
+
+**Чего задача не трогает:** i18n, кроме удаления ключа `pdf.internal_dimensions`
+из словарей; ...
@@ Контракт, п.1 @@
-... становится общим.
+... становится общим. `docs/PDF-EXPORT.md`: фраза про numbered callouts
+заменяется на «значение, которому не хватает места у ребра, не печатается»,
+упоминание callouts в перечне состава сцены снимается.
@@ Контракт, п.5 @@
-5. **Отказ «fail closed» сохраняется** для архитектуры, которая не влезает ни
- в один масштаб (#53). Существующий тест ... не удаляется молча.
+5. **Отказ «fail closed» сохраняется только для архитектуры**, которая не
+ влезает ни в один масштаб (#53); формулировка в `docs/PDF-EXPORT.md`
+ сужается соответственно. Существующий тест ... не удаляется молча.
+6. **Мутант `pdf-room-edge-dropped` переоформляется.** Сегодня он защищает
+ гарантию «каждое неукороченное ребро сохраняет прямое значение или
+ нумерованный вынос», которую отменяет п. 1. Его `because` и гард
+ переписываются под новую гарантию (значение печатается там, где
+ помещается, и не печатается дважды) либо мутант снимается с явной
+ записью — решение принимается при реализации и называется в хендоффе.
@@ AC-таблица @@
+| AC7 | `docs/PDF-EXPORT.md` описывает новое поведение: ни «numbered
+ callouts», ни отказ из-за неразмещаемой аннотации | тест свежести
+ документации на слова-маркеры либо проверка ревьюером построчно |
+ расхождение документа и кода — находка ревью |
@@ Откат @@
-Один revert: возвращаются блок выносов, метки и прежние кегли; golden-кадры
-и два теста восстанавливаются из истории. ...
+Один revert: возвращаются блок выносов, метки, прежние кегли и прежний текст
+`docs/PDF-EXPORT.md`; golden-кадры и два теста восстанавливаются из истории. ...
```
Больше ничего в теле issue не изменилось (сверено полным `diff -u` двух
снимков — выше единственные пять хвостов различий, плюс безобидное «нет
финального перевода строки» в самом старом снимке).
## Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| **Medium** — правка меняет два поведения, задокументированные в `docs/PDF-EXPORT.md` (строки 32/58-59 про callouts, строки 41-42 про fail-closed), но документ не назван ни в скоупе, ни в release-артефактах | Добавлен абзац **«Документация»** в шапку ТЗ с прямой цитатой обеих исходных фраз документа и указанием, что они правятся тем же коммитом; п.1 контракта получил конкретную замену фразы про numbered callouts; п.5 сужен явно («только для архитектуры... формулировка в `docs/PDF-EXPORT.md` сужается соответственно»); добавлен **AC7**, прямо требующий отсутствия обеих устаревших формулировок в документе, с указанным способом доказательства | Тело issue #530, абзац «Документация» (после раздела «ТЗ»); Контракт п.1 и п.5; строка AC7 в таблице AC |
| **Low** — мутант `pdf-room-edge-dropped` защищает гарантию, которую AC1 отменяет, и его судьба не названа | Добавлен **п.6 контракта**: мутант либо переоформляется под новую гарантию, либо снимается — решение и его запись обязательны в хендоффе реализации, то есть решается явно, а не молчанием | Тело issue #530, Контракт п.6 |
Обе находки закрыты предметно (конкретной правкой текста, а не общим
заявлением автора) — проверено чтением дельты выше, не пересказом комментария.
## Дельта: что перепроверено заново
Дельта не задевает продуктовую рамку, контракт п.2–п.4, AC1–AC6, риски,
блок допущений и таблицу критериев полного трека — эти AC перепроверке не
подлежат в этом раунде (см. «Унаследовано из r1»).
Перепроверено:
- **Абзац «Документация» и правки п.1/п.5.** Сверено с текущим текстом
`docs/PDF-EXPORT.md` (прочитан целиком, строки 29-59 — раздел «Sheet and
measurement rules»): обе цитаты в ТЗ («required values that cannot fit
beside an edge use numbered callouts», строка 58-59; «If a fixed annotation
itself cannot fit on one A4 sheet, export stops with an error», строки 41-42)
совпадают с документом дословно. Раздел «Release-артефакты» после правки
всё ещё называет только два бюллетеня changelog и не включает документацию
явным пунктом — но абзац «Документация» в шапке ТЗ и п.1/п.5 контракта
закрывают именно то требование DoR §2.5 («документация... либо явное "нет"»),
на которое указывала находка r1: документ назван, что в нём меняется —
указано текстуально. Формального отдельного пункта в самом
«Release-артефакты» нет, но это не тот же дефект: r1 требовал, чтобы
задача **знала**, что трогает канон, и зафиксировала это — так и сделано,
двумя явными местами в тексте, а не одним. Дробить находку второй раз за
место расположения абзаца было бы формализмом без нового риска для DoR.
- **AC7.** Проверяем самостоятельно, так как в r1 не существовал.
Утверждение проверяемо построчным чтением: после правки в `PDF-EXPORT.md`
не должно остаться ни фразы про «numbered callouts», ни фразы про отказ
из-за неразмещаемой фиксированной аннотации, кроме случая «архитектура не
влезает ни в один масштаб». Способ доказательства назван («тест свежести
документации... либо проверка ревьюером построчно») — это ровно два
метода, разрешённых DoR/§2.7 для этого класса критерия (`unit`-тест на
маркерные слова, либо `«ревью кода»`/чтением). Формулировка «либо» не
делает AC недоказуемым: она называет запасной путь на случай, если
автоматический тест на слова-маркеры не будет написан, а не оставляет
открытым вопрос, что именно проверяется.
- **П.6 контракта (мутант).** Прочитан `scripts/mutation-gate.mjs:224-247`
повторно: `pdf-room-edge-dropped` — единственный мутант, ссылающийся на
тест `dense non-rectangular rooms keep mandatory dimensions in stable
callouts`, который AC1 переписывает на противоположное утверждение. Два
новых мутанта из AC1/AC2 (`pdf-restores-dimension-callouts`,
`pdf-keeps-large-in-plan-labels`) покрывают ту же зону риска (столбец не
возвращается; подписи не растут обратно) — значит, снятие старого мутанта
без замены не оставляет гарантию непокрытой, и формулировка «переоформляется
либо снимается» не открывает дыру в защите, а фиксирует, что она перенесена.
- **Язык замены фразы.** П.1 даёт русскую формулировку-замену
(«значение, которому не хватает места у ребра, не печатается») для
англоязычного документа. Не находка: весь текст ТЗ русский и описывает
предполагаемую английскую формулировку по смыслу, а не диктует точную
строку символ-в-символ — точная английская фраза прямо оставлена
реализации (AGENTS.md: не-продуктовая формулировка — техническая свобода
автора кода), и AC7 проверяет результат («нет старых фраз»), а не текст
замены дословно.
Ни один из пересмотренных пунктов не порождает новой High- или
Medium-находки.
## Унаследовано из r1
Без повторной проверки в этом раунде — документ `docs/reviews/SPEC-REVIEW-530-r1.md`,
материал `Тело issue: 3c6248e08...8135ac` (совпадение подтверждено выше):
- продуктовая рамка §7.1 (персона, поверхность, момент, «до/после» одной фразой);
- выбор полного трека и оба названных нарушенных критерия §5 (перф-бюджет
200 мс, смена UX-контракта — ориентация и потеря раздела);
- все количественные утверждения о текущем коде: `PDF_SCALE_SERIES`, кегли
7/8/9-7/6/8/6, хром страницы 14/10/8/7, арифметика ×0.75;
- AC1–AC6 по существу (состав, формулировка, способ доказательства и мутанты
каждого) — дельта их текст не меняла;
- содержание блока «Принято предположительно» (пилообразное заполнение вне
задачи, поля/шапка/подвал не трогаются, выноска у стены — не эта задача);
- риски (кегль 5.25 pt на грани практики, потеря семи коротких размеров,
необходимость пересъёмки golden на Linux CI, смена ориентации на книжную);
- откат — один revert (дельта лишь добавила упоминание `docs/PDF-EXPORT.md`
в перечень возвращаемого, не изменив механику).
## Что проверено и корректно (этот раунд)
- Обе находки r1 закрыты предметно, текстом, а не декларацией — таблица выше.
- Новый AC7 сформулирован проверяемо и называет способ доказательства из
разрешённого набора.
- Новый п.6 контракта не оставляет гарантию, отменяемую AC1, без замены —
два новых мутанта AC1/AC2 покрывают тот же риск.
- Цитаты ТЗ из `docs/PDF-EXPORT.md` (строки 41-42, 58-59) сверены с файлом
дословно — расхождений нет.
- Дельта не расширяет скоуп и не меняет ни одного количественного значения
из AC1–AC6.
## Чего не проверял
- Не переоткрывал разделы, не затронутые дельтой (продуктовая рамка, AC1–AC6,
риски, блок допущений) — согласно PROCESS §2.10 п.4, это наследуется из r1
без повторной проверки; см. раздел выше.
- Не проверял `demo/smoke_pdf_export.mjs` и golden-фикстуры повторно — дельта
их не касается, а r1 уже отметил их как непроверенные построчно (без
видимого зазора при беглом grep) и это не изменилось.
- Не запускал никаких гейтов (`tsc`, `test`, `build`) — этап ТЗ, продуктового
кода ещё нет, оценивать нечем; так же было отмечено в r1.
- Не проверял точность самих числовых замеров аналитики S2 повторно — это
вне дельты, r1 объяснил, что это воспроизводимый прогон Node-скрипта, а не
предмет ревью ТЗ.
## Вердикт
Зелёный. Обе находки предыдущего раунда (Medium — документация не названа в
скоупе/release-артефактах; Low — судьба мутанта `pdf-room-edge-dropped` не
решена) закрыты предметной правкой текста ТЗ, а не заявлением. Дельта не
вносит новых High- или Medium-находок: новый AC7 проверяем, новый п.6
контракта не оставляет отменяемую AC1 гарантию без замены. Остальное ТЗ
(продуктовая рамка, AC1–AC6, риски, блок допущений, откат) не затронуто
дельтой и наследуется из r1 без повторной проверки.
---
Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0
---
## Материал раунда
- Тело issue #530: снимок `2026-09-11T09:48:17Z` (текущее состояние на момент
ревью), сверено побайтово (`diff -u`, единственное расхождение — конечный
перевод строки).
- Предыдущий материал (r1): тело issue, снимок `2026-09-11T09:37:32Z`,
`sha256: 3c6248e088a3948ba1b6a44fafbc15118b8ad7706edb941b92b3fd9a4c8135ac`
— совпадает с якорем `docs/reviews/SPEC-REVIEW-530-r1.md`.
- Источник снимков: GraphQL `Repository.issue.userContentEdits.nodes[].diff`
(5 правок автора Matysh между `09:15:46Z` и `09:48:17Z`), так как тело issue
правится на месте и `git diff <SHA>` неприменим к этапу spec.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `dev`, коммит `af2081cbc40e` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `08815814f33b6c836ecbd32deabf793353fc1bb1`
```
git log --all --format='%H %T' | grep 08815814f33b
```
- Тело issue: `53fbdfb3b618a562787718e73ee5aa952eda2d9846768cdac42273c2de7ec382`
- Вердикт конвейера: `green` · High 0