20 KiB
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:
- якорь материала r1 в конце
docs/reviews/SPEC-REVIEW-530-r1.md:Тело issue: 3c6248e088a3948ba1b6a44fafbc15118b8ad7706edb941b92b3fd9a4c8135ac; - история правок тела issue вытянута через GraphQL
(
Repository.issue.userContentEdits, полеdiff= полный снимок тела на момент каждой правки, 5 снимков, автор — Matysh, последняя правка2026-09-11T09:48:17Z); sha256sumснимка2026-09-11T09:37:32Z(правка, сделанная непосредственно перед комментарием «ТЗ готово — на ревью»,09:37:50Z) —3c6248e088a3948ba1b6a44fafbc15118b8ad7706edb941b92b3fd9a4c8135ac— совпал побайтово с якорем r1. Это доказывает, что снимок и есть точный текст, получивший вердикт r1, а не приближение;diff -uэтого снимка с текущим телом issue (снимок09:48:17Z, совпадает с текущим телом issue с точностью до конечного перевода строки) дал дельту раунда — приведена целиком ниже.
@@ раздел «ТЗ», шапка @@
-**Чего задача не трогает:** 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.
Материал раунда
- Ветка:
dev, коммитaf2081cbc40e— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
08815814f33b6c836ecbd32deabf793353fc1bb1git log --all --format='%H %T' | grep 08815814f33b - Тело issue:
53fbdfb3b618a562787718e73ee5aa952eda2d9846768cdac42273c2de7ec382 - Вердикт конвейера:
green· High 0