mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-30 19:58:50 +00:00
@@ -0,0 +1,197 @@
|
||||
# SPEC-REVIEW-509-r2
|
||||
|
||||
Issue: [#509](https://github.com/Matysh/houseplan-card/issues/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](https://github.com/Matysh/houseplan-card/blob/dev/docs/reviews/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](https://github.com/Matysh/houseplan-card/blob/dev/docs/reviews/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
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `dev`, коммит `57f38fedbaed` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `614aeecffc19c8ec89e25e908dafb13556a0f286`
|
||||
```
|
||||
git log --all --format='%H %T' | grep 614aeecffc19
|
||||
```
|
||||
- Тело issue: `6224b491f7bef6e6962692c3c901bf5121432fd1e0244740514a420aff96ba36`
|
||||
- Вердикт конвейера: `green` · High 0
|
||||
Reference in New Issue
Block a user