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

20 KiB
Raw Blame History

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 с точностью до конечного перевода строки) дал дельту раунда — приведена целиком ниже.
@@ раздел «ТЗ», шапка @@
-**Чего задача не трогает:** 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 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 08815814f33b6c836ecbd32deabf793353fc1bb1
    git log --all --format='%H %T' | grep 08815814f33b
    
  • Тело issue: 53fbdfb3b618a562787718e73ee5aa952eda2d9846768cdac42273c2de7ec382
  • Вердикт конвейера: green · High 0