Files
houseplan-card/docs/reviews/SPEC-REVIEW-366-r2.md
T
2026-08-29 07:48:53 +00:00

17 KiB
Raw Blame History

SPEC-REVIEW-366-r2

Issue: #366 — «Glow #20: движущиеся ворота на cover-сущности вызывают до ~100 пересчётов геометрии света за одно открытие» Этап: ТЗ на ревью (S4-spec-review), лёгкий трек (small) Заход: r2 · блокирующих циклов ревью ТЗ израсходовано 1 из 2 (лимит §4 для лёгкого трека) Материал: тело issue #366 на момент ревью (ревизия 2), дерево dev на SHA c50d9e42903921589eca8dc90c113ca329e3c6f5 Предыдущий раунд: docs/reviews/SPEC-REVIEW-366-r1.md, вердикт жёлтый, материал — дерево dev на SHA f2e365726a6e28a9acf03b6a37b4d2099770229d Вердикт: зелёный

Скоуп раунда

Разбор по дельте (§2.10 PROCESS.md), а не заново. Артефакт ТЗ живёт в теле issue (small-трек), поэтому формального git diff файла нет; дельта установлена тремя способами и все три сходятся:

  1. футер ревизии 2 сам перечисляет изменения: «M1 — AC4 называет реальный smoke_glow.mjs... M2 — визуальная ступенчатость явно помечена как принятое предположение... Low: строки уточнятся по факту в код-ревью; docs/LIGHT.md §Caching добавлен в release-артефакты реализации»;
  2. прямое сравнение текущего тела issue с фрагментами, процитированными в SPEC-REVIEW-366-r1.md: разделы «Проблема», «Направление фикса», «AC-кандидаты», К1, К2, AC1/AC2/AC3/AC5, «Откат» byte-идентичны цитатам r1 — менялись только формулировка AC4 и добавленный блок «Принято предположительно»;
  3. git diff f2e365726a6e28a9acf03b6a37b4d2099770229d..HEAD --stat -- src/logic.ts src/houseplan-card.ts demo/smoke_glow.mjs demo/smoke_junction_limits.mjs docs/LIGHT.md — пусто. Дерево dev для всех файлов, на которые опирался r1, не сдвинулось ни на строку между SHA r1 и текущим HEAD (три коммита между ними — доки по #369 и публикация самого SPEC-REVIEW-366-r1.md, продукта не касаются). Значит это не ребейз на ушедший вперёд dev (§2.10 «разбор остаётся полным, если...») — условие для сокращённого разбора выполнено, а не просто заявлено.

Дельта локальна и мала по объёму относительно исходного ТЗ: правка текста одного AC и добавление одного маркирующего абзаца. Новая подсистема не задета, контракт (К1/К2, формула квантования, единственная точка применения) не менялся. Полный повторный разбор не требуется.

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

Дельта проверена по существу, не на слово автора:

  • M1 (было: AC4 называл несуществующий smoke_door_glow* плюс необоснованный smoke_junction_limits). Текущий AC4: «существующие смоки/golden #20 зелёные без правки ассертов — прежде всего demo/smoke_glow.mjs (строки ~598-631 уже гоняют current_position 0/50/100 — все узлы сетки 0.05) плюс полный смок-набор по smoke-select дельты». Прочитал demo/smoke_glow.mjs:598-631 — setDoorPosition(0) на :627 и setDoorPosition(50) на :629, начальный current_position: 100 на :360 (glow_dynamic_door через current_position реально существует и реально использует доли 0/0.5/1.0 — узлы сетки 0.05 из К1). Название файла и утверждение о его содержимом точны. Ссылка на smoke_junction_limits без обоснования убрана, вместо неё — отсылка на инструмент выбора смоков по дельте (scripts/smoke-select.mjs), который и предназначен закрыть этот класс вопроса, когда код появится. M1 закрыта по существу, не просто переформулирована.
  • M2 (было: «5% неразличимо» подано фактом, не пометкой). Текущий текст содержит отдельный абзац: «Принято предположительно (r1-M2): визуальная цена — вырез света ступает по 5% длины проёма. Предположение: ... Если полевые впечатления владельца после беты скажут обратное — квант уменьшается правкой одной константы (0.02 ⇒ ≤51 пересчёт) либо решение пересматривается в сторону debounce; цена отката — один revert.» Это ровно формат «принято предположительно, поменять свободно» из §7.1, названо явное условие пересмотра и цена отката. M2 закрыта.
  • L1 (номера строк в ТЗ отстали от dev). Автор явно отложил до код-ревью («строки уточнятся по факту»), не стал переписывать текст ТЗ. Проверил актуальность: поскольку houseplan-card.ts не менялся между SHA r1 и текущим HEAD (см. diff --stat выше), расхождение то же самое, что было на r1 (было −1 к номеру), не выросло. Отложенное решение приемлемо: Low находится на усмотрение ревьюера, а номера строк реализация всё равно сверит перед кодом.
  • L2 (docs/LIGHT.md не назван в release-артефактах). Футер ревизии 2 утверждает «docs/LIGHT.md §Caching добавлен в release-артефакты реализации» — проверил текст всего тела issue (grep -in "light.md\|release" по полному телу): единственное вхождение LIGHT.md — сама эта фраза в футере. В основном тексте ТЗ (раздел «User-Visible») по-прежнему только «changelog en+ru», отдельного упоминания docs/LIGHT.md в тексте контракта или отката нет. Заявление футера не подтверждено текстом документа — см. находку L2′ ниже.
  • К1/К2/AC1/AC2/AC3/AC5/откат — не перепроверялись заново по существу (наследуются из r1, см. раздел ниже), т.к. дельта их текста не касается и дерево dev, от которого зависела их проверка, не двигалось.

Находки

Находок, блокирующих цикл (High или Medium в скоупе без ответа), нет.

L2′ (Low, не блокирует) — расхождение футера ревизии 2 с фактическим текстом ТЗ

Футер ревизии 2 заявляет, что docs/LIGHT.md §Caching добавлен в release-артефакты реализации. По факту в теле issue такой записи нет — раздел «User-Visible» называет только changelog en+ru, отдельного упоминания docs/LIGHT.md как обязательного release-артефакта нет ни в контракте, ни в разделе отката.

Почему Low: предыдущая находка L2 (r1) была необязательной («правится либо снимается решением ревьюера с записью, если сочтёт достаточным changelog»). Обновление канонического документа подсистемы при смене контракта сигнатуры всё равно требуется по DoR (§2.5 «release-артефакты... документация») и будет проверено на код-ревью независимо от текста ТЗ — неточность футера не создаёт риска пропустить реальную правку LIGHT.md, только неточно описывает, что уже сделано на этапе ТЗ.

Диспозиция: снимаю без правки текста ТЗ, с записью здесь. Требование к разработчику остаётся: при реализации обновить docs/LIGHT.md §Caching числовым шагом квантования (0.05) тем же коммитом, что и код — это проверит код-ревью (S7), а не повторный цикл ревью ТЗ.

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

Находка (r1) Чем закрыта Где видно
M1 — AC4 называет несуществующий/необоснованный смок AC4 переписан: называет реальный demo/smoke_glow.mjs (проверено: строки 598-631 действительно двигают current_position через 0/50/100, узлы сетки 0.05) + отсылка к smoke-select.mjs вместо необоснованного smoke_junction_limits Тело issue #366, раздел «AC и доказательства», пункт AC4
M2 — визуальная ступенчатость подана фактом, не предположением Добавлен абзац «Принято предположительно (r1-M2)» с явным условием пересмотра и ценой отката Тело issue #366, между К2 и «AC и доказательства»
L1 — номера строк в ТЗ отстали от dev Не исправлено текстом; автор явно отложил до код-ревью. Расхождение не выросло — houseplan-card.ts не менялся с r1 (см. git diff --stat в разделе «Как проверялось») Футер ревизии 2: «строки уточнятся по факту в коде-ревью»
L2 — docs/LIGHT.md не назван в release-артефактах Не закрыта фактически, несмотря на заявление футера — см. находку L2′. Снимается как Low с явной запиской, требование переносится на код-ревью Футер ревизии 2 vs тело issue (нет строки в разделе User-Visible)

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

Без повторной проверки принято из docs/reviews/SPEC-REVIEW-366-r1.md (SHA f2e365726a6e28a9acf03b6a37b4d2099770229d), поскольку дельта этого раунда их текста не касается, а код, на котором строилась проверка, не изменился (git diff --stat пуст для src/logic.ts, src/houseplan-card.ts, demo/smoke_glow.mjs, docs/LIGHT.md между тем SHA и текущим HEAD):

  • Локализация проблемы (openingLightStateSignature, src/logic.ts:344-357, amount.toFixed(3)) и её причина (cover current_position в процентах даёт новую сигнатуру на каждый процент хода);
  • Соответствие docs/SCOPE.md (починка перф-дефекта в рамках J1/J7, не новая функция) и обоснованность small-трека (одна поверхность, геометрия/миграции не тронуты);
  • К1 — единственная точка квантования (passageStates, src/houseplan-card.ts:10352-10361 на момент r1) реально питает и сигнатуру, и длину выреза, и partition-cut — принцип «одно число — один источник» (§8 PROCESS.md) соблюдён архитектурно;
  • К2 и математика квантования: сетка 0.05 на [0,1] даёт ровно 21 узел; Math.round(clamp(x)/0.05)*0.05 даёт 0.30/0.31 → один и тот же узел, 0.30/0.33 → разные, NaN/-1/2 → корректный зажим — проверено в r1 прямым расчётом, формула в теле issue не менялась;
  • AC1 (юнит на quantizeOpeningLightAmount), AC2 (≤21 сигнатура на свипе), AC3 (binary-двери байт-в-байт — квантование стоит выше openingLightStateSignature, существующие юниты её не заденут), AC5 (мутант «квант → identity» красит AC2 детерминированно) — тексты AC1-3,5 не менялись между r1 и r2, проверка r1 остаётся в силе;
  • Откат (один revert, кэш самоинвалидируется сигнатурой, конфиг не участвует) — текст не менялся;
  • User-Visible: yes с обоими changelog — учтено верно (с оговоркой L2′ про LIGHT.md выше, которая новая для этого раунда).

Что проверено и корректно (этот раунд)

  • Обе Medium-находки r1 закрыты предметно: правка текста, а не риторическая переформулировка — проверено чтением файла-доказательства (smoke_glow.mjs) и текста добавленного абзаца допущения;
  • Дерево dev, от которого зависела проверка r1, не двигалось — условие «разбор остаётся полным при ребейзе» неприменимо, сокращённый разбор правомерен;
  • Новых Medium/High находок делта не создала.

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

  • Код не написан — typecheck/test/build, npm run golden:verify, node scripts/model-invariants.mjs, смоки браузером — не мой гейт на этапе ревью ТЗ, предмет S7 (код-ревью). Дешёвые гейты не гонял: делта этого раунда — текст issue, src/** не тронут.
  • Не проверял глазом визуальную неразличимость 5%-ступени в браузере — унаследовано из r1 как оценка правдоподобия, теперь явно помеченная в самом ТЗ как предположение с планом пересмотра, что и было целью M2.
  • Не проверял, будет ли docs/LIGHT.md реально обновлён при реализации — это требование переносится на код-ревью (см. L2′).

Вердикт

Обе блокирующие Medium-находки r1 закрыты по существу и проверяемо. Новых находок делта не породила, кроме одной некритичной (L2′, footer расходится с телом issue, снята с запиской). Технический контракт и AC остаются доказуемыми в объёме, установленном r1. Issue готово к переходу в S5-ready.