19 KiB
SPEC-REVIEW-509-r2
Issue: #509 — «Сводная панель: при первом показе подвисание и «Source unavailable» вместо значений до загрузки metrics-чанка». Этап: spec (PROCESS.md §2.4). Заход: r2. Блокирующих циклов израсходовано 1 из 4 (потрачен жёлтым вердиктом r1; зелёный цикла не образует, #227).
Материал: тело issue #509, раздел ## ТЗ, редакция после комментария автора
2026-09-10T08:07:55Z («ТЗ r2 — обе находки закрыты»). Предыдущий заход —
SPEC-REVIEW-509-r1.md,
материал того раунда: ветка dev d204bf5d7737, дерево cf36bd3b2a3141c427a40f9e10aac607faf02a1f,
тело issue sha256 8d514ac196bbc453d5b25b352421dc27f9925b8668a89e44c00971f304f2c3e4,
вердикт yellow · High 0 · Medium 2.
1. Скоуп ревью
Заход r2, не первый — разбор по дельте (PROCESS.md §2.10). Дельта не затрагивает ребейз (кода по задаче ещё нет вовсе), не меняет контракт целиком и не задевает новую подсистему — она ровно закрывает три находки r1. Разбор поэтому сокращён до дельты плюс всего, до чего эта дельта дотягивается.
Объявление дельты (сверка текущего тела с текстом, разобранным в r1):
- в преамбулу
## ТЗдобавлено предложение с явным критерием §5, который задача не проходит; - в конец ТЗ добавлен раздел «Принятые технические предположения» (4 пункта);
- контракт п.4 переформулирован: вместо «микрозадача/
requestIdleCallbackс фолбэком на таймер» — точный механизмsetTimeout(0)→requestAnimationFrame→setTimeout(0)со ссылкой на новый раздел допущений и на новый AC8; - добавлены AC8 (бюджет времени от скелета до значения) и AC9 (прежнее значение при инвалидации мемо, без скелета);
- строка таблицы «Чем краснеет» для защиты «прежнее значение при инвалидации» теперь указывает на AC9 (в r1 эта строка на AC не ссылалась — сама защита не была доказана ни одним AC).
Остальной текст (Сценарий, Что человек увидит, Не-скоуп, AC1–AC7, Риски, Откат, Release-артефакты, персона, i18n) не менялся — эти AC и утверждения наследуются из r1 без повторной проверки (см. §4 ниже).
2. Как проверялось
- Текущее тело issue #509 получено
gh issue view 509 --json body,comments,labels,state. - Полностью прочитан
docs/reviews/SPEC-REVIEW-509-r1.md, включая блок «Материал раунда». - Прочитан комментарий-закрытие автора (
2026-09-10T08:07:55Z). - Каждая из трёх находок r1 (Medium-1 из трёх пунктов, Medium-2, Low-1) сверена построчно с новым текстом — не принята на слово по заявлению автора «обе находки закрыты» (см. таблицу §3): по каждому пункту процитирована конкретная строка ТЗ, которая его закрывает, а не факт наличия нового раздела как такового.
- Проверено, что новый раздел «Принятые технические предположения» оформлен в формате, который требует PROCESS.md §7.1 («принято предположительно, поменять свободно»), а не как скрытая догадка, поданная за решение.
- Проверено
docs/USER-GUIDE.ru.md(раздел «Сводная панель», строки 255–294) — новый визуальный термин «скелет»/пульсирующий прямоугольник не вводит нового пользовательского текста и не расходится с зафиксированной терминологией: раздел вообще не описывает переходные состояния загрузки, конфликта нет; заявление ТЗ «новых i18n-ключей нет» по-прежнему верно (текст не добавляется, только визуальный плейсхолдер). - Подтверждено, что кода по задаче всё ещё не существует:
git ls-remote --heads origin 'issue/509-*'— пусто;HEAD=57f38fed(«docs: review document for #509», т.е. коммит публикации r1, не код). Гейтыtypecheck/test/build/смоки на этом этапе неприменимы — как и в r1. - Подтверждено, что метки issue не изменились:
bug,S4-spec-review, безsmall— полный трек по-прежнему корректен и теперь явно обоснован в тексте (закрытие Low-1).
3. Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
Medium-1, п.1 — механизм переноса расчёта вне кадра назван как альтернатива («микрозадача/requestIdleCallback»), а не решение; ни один AC не ограничивал время до появления значения |
Механизм назван однозначно: setTimeout(0) → requestAnimationFrame → setTimeout(0); requestIdleCallback явно отвергнут с причиной (откладывается на неопределённое время под нагрузкой); время ограничено новым AC |
Раздел «Принятые технические предположения», п.1 «Момент расчёта…»; Контракт п.4 (переформулирован); AC8 |
| Medium-1, п.3 — форма хранения «прежнего значения» при инвалидации мемо не названа | Названа: «stale-while-revalidate» — мемо не сбрасывается при инвалидации, старое значение остаётся на экране до завершения фонового пересчёта | Раздел «Принятые технические предположения», п.2 «Stale-while-revalidate»; согласуется с новым AC9 |
| Medium-1, п.2 — способ технической инъекции счётчика вызовов для AC3 не назван ни как решение, ни как «на усмотрение реализации» | Не закрыт текстом ТЗ. Формулировка AC3 не изменилась («унит со счётчиком вызовов через инъекцию»). Разобран отдельно ниже как Low-2 и снят мной с записью — см. §5 | — |
| Medium-2 — защита «при инвалидации мемо панель показывает прежнее значение, а не скелет» не доказана ни одним AC1–AC7 | Новый AC9 прямо проверяет два состояния: мемо устарело → ready со старым значением + запланированный пересчёт; мемо нет → pending. Мутация «мемо сбрасывается в скелет» теперь имеет названного свидетеля |
AC9; таблица «Чем краснеет», строка «прежнее значение при инвалидации» → «unit (AC9)» |
| Low-1 — критерий §5, который задача не проходит, не назван явно при выборе полного трека | Назван дословно: «Критерий §5, который задача не проходит: «нет влияния на производительность и бюджеты» — здесь оно и есть предмет задачи» | Преамбула ## ТЗ, сразу после заголовка |
4. Унаследовано из r1
Без повторной проверки принимается всё, чего дельта r2 не касается — со
ссылкой на SPEC-REVIEW-509-r1.md,
материал: dev d204bf5d7737 / дерево cf36bd3b2a3141c427a40f9e10aac607faf02a1f
/ тело issue sha256 8d514ac196bbc453d5b25b352421dc27f9925b8668a89e44c00971f304f2c3e4:
- фактические утверждения ТЗ о текущем коде, перепроверенные там чтением
построчно:
LoadedSummaryPanelRuntime.value()(summary-panel-runtime-loaded.ts:715-720),metrics()(:687-712),totalCleanFloorAreaM2(summary-panel-metrics.ts:59-84),innerContourForRoom(wall-thickness.ts:2750-2779), приём общей геометрии карточкой через_innerRoomContour/_wallUnionGeometry()(houseplan-card.ts:9219-9240), существование и подключениеSummaryPanelPresentation(#505, закрыт и смёржен), реальность i18n-ключаsummary.unavailableна 4 языках; - соответствие персоны и поверхности
docs/SCOPE.md(Household members, View/kiosk, J1) и отсутствие конфликта со SCOPE; - присутствие обязательных разделов §7.1 по существу (таблица r1, §2);
- техническая реализуемость AC3 в части передачи общей геометрии (
innerContourForRoomуже принимает shared-параметры как опциональные — инженерная перестановка вызовов, а не гипотеза); - отсутствие иных незаявленных вопросов владельцу — единственный вопрос был закрыт дефолтом («вариант A») ещё до r1;
- оценка рисков (флейк порога AC4, единичные кадры «недоступности» для
entity-источников) и отката (правка локальна для
summary-panel-*) — дельта их не касается.
Эти пункты не перепроверялись повторно, так как дельта r2 не вносит правок в описываемый ими код или текст.
5. Находки (r2)
Low-2 (снимается записью) — способ инъекции счётчика вызовов для AC3 остался неназванным
AC3 по-прежнему формулирует доказательство как «унит со счётчиком вызовов
через инъекцию», не уточняя механизм, хотя wallBodiesGeometry и
multiWallNodesForGeometry — обычные экспортируемые функции ES-модуля, а не
параметры конструктора или поля объекта (это же отмечено в Medium-1 r1,
п.2). Формально пункт 2 находки r1 не закрыт: раздел «Принятые технические
предположения» решает пункты 1 и 3, но не пункт 2.
Почему я снимаю это, а не возвращаю жёлтым. Способ подсчёта вызовов —
чистая стратегия теста, а не решение, которое видит пользователь или которое
меняет контракт поведения хотя бы одного AC; PROCESS.md §7.1 прямо относит
«стратегию тестов» к тому, что «агенты решают сами». Технически это
стандартный приём (vi.spyOn на неймспейс модуля wall-thickness.ts при
сохранении остальной реализации, либо vi.mock с importOriginal и
частичной заменой двух экспортов) — он не требует нового паттерна в
продуктовом коде и не был поставлен под сомнение как нереализуемый ни в r1,
ни сейчас. Формальный пробел реален, но не блокирует ни имплементируемость,
ни проверяемость AC3 по существу, поэтому не стоит третьего цикла ревью
ради одной фразы в способе доказательства. Автору стоит при реализации явно
выбрать один из вариантов (vi.spyOn на неймспейс либо обёртку с DI) —
запись здесь фиксирует это как открытый, но не блокирующий пункт.
6. Что проверено и корректно
- Medium-1 (пп.1, 3) и Medium-2 закрыты по существу: не косметическим дополнением текста, а конкретными новыми AC (AC8, AC9), встроенными и в контракт (п.4 ссылается на AC8 и на раздел допущений), и в таблицу «Чем краснеет» — а не оставлены висеть отдельно от AC, как предупреждал прецедент Medium-1 r1.
- Low-1 закрыт дословной формулировкой критерия §5, без домысливания.
- Новый раздел «Принятые технические предположения» оформлен ровно в формате PROCESS.md §7.1 — «принято предположительно, менять свободно, ревьюер вправе оспорить» — а не как факт о будущем коде, поданный без пометки.
- Ширина скелета (≈4.5em) — единственная новая деталь в разделе допущений, которая формально видна пользователю (ширина плейсхолдера), но это не продуктовый вопрос по смыслу §7.1 (не «что человек видит и делает» на уровне сценария, а стилевая деталь уже согласованного визуального решения), и она прямо помечена как оспоримое предположение — эскалации владельцу не требует.
- Никакой новой догадки, поданной как факт вне пометки, в дельте не найдено.
- Терминология не расходится с
docs/USER-GUIDE.ru.md— раздел «Сводная панель» не документирует переходные состояния, конфликтовать нечему; новых i18n-ключей действительно нет. - Метки issue (
bug,S4-spec-review, полный трек) согласованы с текстом. - Мелкая редакционная деталь:
AC8в списке идёт доAC7(правка вставила новые пункты перед последним исходным) — это не влияет на уникальность номеров и не порождает двусмысленности, поэтому не считаю это находкой.
7. Чего не проверял
- Не перепроверял чтением код
value(),metrics(),totalCleanFloorAreaM2,_innerRoomContour,SummaryPanelPresentation, ключsummary.unavailable— дельта r2 не меняет ни одного утверждения об этих местах кода, инвентарь r1 наследуется (§4). - Не прогонял
typecheck/test/build/смоки/golden/performance_smoke— кода по задаче нет вообще (веткаissue/509-*не создана), эти гейты относятся к этапуcode, а неspec. - Не проверял синтаксическую реализуемость
setTimeout(0)→requestAnimationFrame→setTimeout(0)в конкретном классе рантайма — это стандартные браузерные API, а конкретная реализация ещё не написана. - Не оценивал качество будущих мутационных гардов таблицы «Чем краснеет» сверх того, что они названы и привязаны к AC — код появится при реализации.
- Не проверял механизм подсчёта
sha256тела issue в блоке якорей документа — инфраструктура конвейера, не предмет ревью ТЗ.
8. Вердикт
High: 0. Medium: 0 (обе находки r1 закрыты по существу новыми AC/разделом). Low: 1 (Low-2 из этого раунда — снят записью в §5; Low-1 r1 закрыт автором).
Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0
Материал раунда
- Ветка:
dev, коммит57f38fedbaed— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
614aeecffc19c8ec89e25e908dafb13556a0f286git log --all --format='%H %T' | grep 614aeecffc19 - Тело issue:
6224b491f7bef6e6962692c3c901bf5121432fd1e0244740514a420aff96ba36 - Вердикт конвейера:
green· High 0