17 KiB
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 файла нет; дельта
установлена тремя способами и все три сходятся:
- футер ревизии 2 сам перечисляет изменения: «M1 — AC4 называет реальный
smoke_glow.mjs... M2 — визуальная ступенчатость явно помечена как принятое предположение... Low: строки уточнятся по факту в код-ревью;docs/LIGHT.md§Caching добавлен в release-артефакты реализации»; - прямое сравнение текущего тела issue с фрагментами, процитированными в
SPEC-REVIEW-366-r1.md: разделы «Проблема», «Направление фикса», «AC-кандидаты», К1, К2, AC1/AC2/AC3/AC5, «Откат» byte-идентичны цитатам r1 — менялись только формулировка AC4 и добавленный блок «Принято предположительно»; 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_position0/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)) и её причина (covercurrent_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.