Files
houseplan-card/docs/reviews/SPEC-REVIEW-509-r2.md
T
2026-09-10 08:14:22 +00:00

19 KiB
Raw Blame History

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 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 614aeecffc19c8ec89e25e908dafb13556a0f286
    git log --all --format='%H %T' | grep 614aeecffc19
    
  • Тело issue: 6224b491f7bef6e6962692c3c901bf5121432fd1e0244740514a420aff96ba36
  • Вердикт конвейера: green · High 0