# 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`.