Files
houseplan-card/docs/reviews/SPEC-REVIEW-329-r2.md
T
2026-08-27 23:29:49 +03:00

19 KiB
Raw Blame History

SPEC-REVIEW-329-r2

Issue: #329 · Этап: spec (S4-spec-review) · Заход: r2 · Трек: обычный · Блокирующих циклов израсходовано до этого раунда: 1/4

ТЗ: docs/specs/329-junction-limits.md. Ревизия под ревью — коммит 783c04d1813b0b201efb21b8bae1b0c497978cf1 («docs: spec #329 revision 2 — per-surface refusal channels, AC5 split, absolute thresholds»), базовый SHA раунда r1 — 8dce8453975121a89263d44243e49059f7000da7. Класс C (docs/specs/**), продуктовый код не тронут — git show --stat 783c04d1 подтверждает единственный файл в диффе.

Скоуп ревью (по дельте, PROCESS.md §2.9)

Раунд не первый. Предыдущий вердикт — жёлтый, r1, найден в комментарии issue claude[bot] от 2026-08-27T16:37:06Z, зафиксирован на SHA 8dce8453 (назван в шапке docs/reviews/SPEC-REVIEW-329-r1.md, committed at 9f7d1fd4). Автор объявил делту в комментарии от 2026-08-27T16:39:52Z: «Ревизия 2 (783c04d1): M1 — ..., M2 — ..., L1 — ...».

Дельта — git diff 8dce8453..783c04d1 -- docs/specs/329-junction-limits.md: 38 строк (29 добавлено, 9 удалено) внутри одного файла, только в §2 (нормативные ограничения/канал отказа) и §8 (AC5 → AC5a/AC5b). Остальной документ (§1, §3 body, §4, §5, §6, §7, AC1–AC4, AC6, AC8–AC10, §9, §10, §11) байт-не тронут diff'ом.

Делта локальна: это не ребейз на ушедший вперёд dev, не смена контракта поведения продукта (та же формулировка решений владельца, что и в r1), не задета новая подсистема. Полный разбор документа не требуется — разбираю делту плюс всё, до чего она дотягивается логически (это как раз и вскрыло находку ниже: обновлённое §2 разошлось с AC7, который delta не тронула).

Как проверялось

  • Прочитан диф git diff 8dce8453..783c04d1 построчно — оба заявленных изменения (M1, M2) сверены с текстом находок r1 дословно; L1 тоже проверен, хотя автор не был обязан его чинить.
  • Перечитан docs/USER-GUIDE.ru.md, раздел «Resize» (строки 473–493, таблица 477–484) и «Толщина стены» (515–528) — сверка новой формулировки канала §2 с уже задокументированным поведением инструментов.
  • git log --all --oneline | grep 313 + git show 5e5dad27 --stat — проверен прецедент #313, на который ссылается §2 для канала «Толщины» (commit message: «Ноль/пусто для независимой кладки отклоняется существующим тостом диапазона») — ссылка не выдумана, поведение реальное.
  • Перепроверена математика AC5b: равносторонний треугольник, сторона a, inradius r = a/(2√3). При a=60: r≈17.32 см, полутолщина стены 33/2=16.5 см → остаток r-half≈0.82 см, внутренний треугольник (offset) имеет сторону a·(r-half)/r ≈ 60·0.82/17.32 ≈ 2.84 см, площадь (√3/4)·2.84²≈3.5 см² — меньше 25 см², П5 отказывает при выполненном П1 (60°≥15°). При a=120: r≈34.64 см, остаток 34.64-16.5=18.14 см, сторона внутреннего треугольника ≈120·18.14/34.64≈62.8 см, площадь ≈1706 см² — намного больше 25 см², проходит. Числа в AC5b (17.3, 34.6, «<25 см²», «проходит») подтверждаются независимым пересчётом. Побочно проверено, что синтетика AC5b не задевает другие правила (П2: 2 стены/узел ≤6; П3: длина стороны 60/120 см ≥ толщины 33 см) — тест действительно изолирует П5 от остальных ограничений, как заявлено.
  • Grep по git show --stat для 783c04d1 — подтверждено, что кроме docs/specs/329-junction-limits.md ничего не менялось: гейты кода (tsc/test/build/check-docs/инварианты/смоки) не относятся к чисто документационной ревизии на этапе spec, не прогонял — так же, как в r1.
  • Перечитаны §3, §6, §7, §9 целиком (не только делта) — чтобы убедиться, что обновлённое §2 не противоречит соседним разделам, которые delta не трогала (см. находку ниже про AC7).

Закрытие раунда r1

Находка r1 Чем закрыта Где это видно
M1 — единый «тост» заявлен как канал отказа для всех пяти поверхностей, включая Resize, что расходится с уже задокументированным контрактом ручек (USER-GUIDE, строки 482–483) §2 переписан: явный список из трёх каналов по поверхностям — тост (рисование/черновики/независимые/merge-split), существующий контракт приглушённой ручки с объяснением по hover/focus/нажатию для Resize (тост явно исключён: «тост не используется»), тост-в-трее по образцу #313 для «Толщины». Формулировка Resize синтезирует уже документированные строки 482–484 USER-GUIDE (упор в последнюю допустимую позицию + приглушение с объяснением), не изобретает новый визуальный канал docs/specs/329-junction-limits.md §2, строки 26–38
M2 — AC5 сливал повтор фикстуры issue (нарушает П1 напрямую, дублирует AC1) и непроверяемую гипотезу «П5 независим от П1» без конкретных чисел AC5 разделён: AC5a — сквозной смок фикстуры issue поверх AC1 (честно назван дублирующим по цели, не по механике: unit vs smoke); AC5b — синтетика с названными числами (сторона 60 см / стены 33 см → отказ по П5 при выполненном П1; сторона 120 см → проходит). Числа проверены пересчётом выше — корректны docs/specs/329-junction-limits.md §8, строки 129–136
L1 (Low, не обязана) — параллель «(1 клетка)» у абсолютных порогов П4/П5 могла увести к масштабированию на cell_cm Скобки убраны, П4 и П5 явно помечены «порог абсолютный ... не зависит от cell_cm» docs/specs/329-junction-limits.md §2, строки 49–50, 52–53

Все три находки закрыты по существу, не переформулированы уклончиво: M1 и M2 дают именно то, что просил r1 (раздельные каналы по поверхности; отдельная синтетика с числами), а не косметическую перефразировку исходного текста.

Находки (r2)

r2-M1 (Medium, в скоупе) — AC7 не обновлён вместе с §2 и не проверяет заявленный канал отказа для Resize/«Толщины»

docs/specs/329-junction-limits.md, §8, AC7 (текст не менялся диффом r1→r2):

«AC7. Resize/«Толщина», приводящие к нарушению любого из П1–П5, — отказ fail-closed, план байт-неизменен.»

M1 в r1 требовал: «Явно расписать, какой канал обратной связи используется на КАЖДОЙ из пяти поверхностей» — и отдельно указывал, что «AC7 (Resize/«Толщина») тоже не называет способ обратной связи ... что расходится с общей формулировкой §2». Автор исправил §2 (теперь там детальный, специфичный по каждой поверхности контракт — вплоть до формулировки «текст объяснения называет нарушенное правило П1–П5» для Resize и «причина показывается тостом с названием правила (П3/П5)» для «Толщины»), но AC7 остался прежним и по-прежнему не тестирует канал — только байт-неизменность плана. §6 (план тестов) тоже не называет конкретный тест на текст ручки/тоста для этих двух поверхностей — там есть только «юниты на каждую формулу П1–П5» и «смок репро-фикстуры #329», которые формулу проверяют, а не канал отказа.

Это ровно тот класс дефекта, который правился в M1: нормативный текст §2 теперь делает более специфичные утверждения о видимом поведении (что именно называет объяснение ручки, что именно показывает трей), чем было в r1 — но ни одна acceptance criteria не привязана к доказательству именно этой специфики, и §9 («принято предположительно») её тоже не перечисляет как допущение.

Чем это грозит. Риск, который в r1 сформулирован как последствие M1 («реализация по букве §2 добавит тост поверх существующей ручки, либо автор тихо перепишет UX Resize»), не устранён полностью: теперь у §2 есть правильный нормативный текст, но у AC7 нет проверки, что реализация ему соответствует. Разработчик, ориентирующийся на AC (а не на прозу §2) при написании тестов на этапе кода, может закрыть AC7 голым fail-closed-тестом, не проверив, что ручка называет П1–П5, а трей — П3/П5, и разойтись с §2 незамеченным до код-ревью или позже.

Что нужно сделать. Дополнить AC7 (или завести AC7a/AC7b по аналогии с AC5) явной проверкой канала: для Resize — текст hover/focus/click называет нарушенное правило (П1–П5) и тост при этом не появляется; для «Толщины» — трей показывает отказ значения с тостом, называющим правило (П3/П5). Либо, если такая гранулярность признаётся избыточной для AC и достаточно прозы §2, явно снять эту находку в комментарии автора с той же аргументацией — но не оставлять молча, раз в этом раунде именно расхождение AC и §2 было предметом M1.

Унаследовано из r1

Не проверялось заново — делта r1→r2 (git diff 8dce8453..783c04d1) эти разделы байт-не тронула, и ни одно новое r2-наблюдение до них логически не дотягивается:

  • Продуктовая рамка (docs/SCOPE.md, соответствие J6/J1/J5/J7) — принято без повторной проверки.
  • §1 (сценарий), §4 (рендер легаси-острия, ссылки на #309/#310 в docs/WALL-THICKNESS.md), §5 (бэкенд, прецедент invalid_partition_opening_jamb_margin) — принято без повторной проверки.
  • §6 (артефакты/поверхности, i18n-ключи, golden-сцена), §7 (производительность/touch) — принято без повторной проверки, за вычетом отмеченного выше пробела AC7↔§2 в §8, который относится к §8, а не к §6.
  • AC1–AC4, AC6, AC8–AC10 — формулировки не менялись диффом, границы и способ доказательства уже проверены в r1 — принято без повторной проверки.
  • §9 («принято предположительно»), §10 (риски), §11 (откат) — не менялись, принято без повторной проверки.
  • Обязательные разделы §7.1 присутствуют по содержанию, артефакт корректного класса (полноценный файл ТЗ, задача не small) — принято без повторной проверки.
  • Пять численных ограничений П1–П5 совпадают с решением владельца в чате issue дословно (проверено в r1) — сами числа delta не меняла (менялась только форма записи П4/П5 без «(1 клетка)», см. закрытие L1 выше), повторная сверка с чатом владельца не требовалась.

Источник: docs/reviews/SPEC-REVIEW-329-r1.md (committed at 9f7d1fd4), вывод получен на SHA 8dce8453975121a89263d44243e49059f7000da7.

Что проверено и корректно (делта r2)

  • M1 закрыт по существу: канал отказа расписан по всем пяти поверхностям отдельно, Resize явно НЕ использует тост (снимает риск дублирующего сообщения из r1), формулировка синтезирует уже задокументированное поведение USER-GUIDE (строки 482–484), а не изобретает новый визуальный контракт.
  • Канал «Толщины» (тост-в-трее по образцу #313) подтверждён существующим прецедентом — не выдумка, коммит 5e5dad27 содержит именно такое поведение для независимой кладки.
  • M2 закрыт по существу: AC5a/AC5b разделены корректно, числа AC5b математически верны (пересчитано независимо, см. «Как проверялось»), синтетика действительно изолирует П5 от П1/П2/П3.
  • L1 закрыт (не был обязателен) — абсолютность порогов П4/П5 теперь explicit, скобка с «клеткой» убрана, не вводит в заблуждение.
  • Никаких новых догадок, выданных за факт, в самой делте (кроме находки r2-M1, которая не про новую догадку в тексте, а про рассинхрон текста и AC) не обнаружено.

Чего не проверял

  • Весь материал, перечисленный в разделе «Унаследовано из r1» — сознательно не перепроверял, делта его не касается (см. обоснование там же).
  • Код не менялся в этом коммите (только docs/specs/**, класс C) — гейты typecheck/test/build/check-docs/инварианты модели/смоки не прогонял: они не относятся к чисто документационной правке на этапе spec-review.
  • Не проверял, как именно разработчик в итоге сформулирует текст ручки/тоста при реализации — это предмет код-ревью, а не спек-ревью; здесь оценивается только наличие проверяемого критерия в ТЗ (которого, по находке r2-M1, для этой специфики пока нет).
  • Не запрашивал у владельца дополнительных продуктовых уточнений — находка r2-M1 технического характера (согласованность AC с прозой), решается автором ТЗ, а не владельцем.

Вердикт

Жёлтый. Один Medium в скоупе задачи (r2-M1), High-находок нет. Оба Medium раунда r1 (M1, M2) закрыты корректно и не переоткрываются. Находка r2-M1 чинится в этом же ТЗ (правка AC7/§8) без смены архитектуры документа; отдельный issue не заводится (#202, находка в скоупе).