mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
@@ -0,0 +1,228 @@
|
||||
# SPEC-REVIEW-520-r3
|
||||
|
||||
## Якоря
|
||||
|
||||
- Issue: [#520](https://github.com/Matysh/houseplan-card/issues/520)
|
||||
«Первый устойчивый кадр большого дома вырос до 3.36 с при потолке 3.0 с —
|
||||
регрессия после #509/#500», метка `S4-spec-review`, состояние `OPEN`
|
||||
(подтверждено `gh issue view 520 --json labels,state`, 2026-09-10).
|
||||
- ТЗ: тело issue #520, раздел `## ТЗ`, продолжает жить в теле issue (#517:
|
||||
архив `docs/specs/` закрыт для новых ТЗ) — то же место, что в r1/r2.
|
||||
- sha256 текущего тела issue (`gh issue view 520 --repo Matysh/houseplan-card
|
||||
--json body -q .body | sha256sum`):
|
||||
`5eca6e230c290483bba634505217d17072f4b4a121c4aa2111b08a0fc6d007c3`.
|
||||
Отличается от хеша r2 (`7f68e4cd99242f44e13e9929cd0a7aa9dc3cb6ae5f871878670
|
||||
bade18a2436ac`) — тело менялось, как и ожидается после возврата на правки.
|
||||
- Заход r3. Бюджет циклов §4 до этого ревью: **2/4** (израсходован r1 и r2,
|
||||
оба жёлтые). Вердикт ниже — зелёный, цикла не образует, бюджет остаётся
|
||||
**2/4**.
|
||||
- Материал кода не менялся между r2 и r3: `git log --oneline ad2f9afd..
|
||||
b49721a0` даёт ровно один коммит — публикацию документа r2
|
||||
(`docs/reviews/SPEC-REVIEW-520-r2.md`, 203 добавленных строки, `git diff
|
||||
--stat ad2f9afd..b49721a0 -- src/ demo/ scripts/ test/` пуст). Рабочая
|
||||
копия сейчас на `b49721a0` (detached HEAD от
|
||||
`origin/issue/520-first-frame`, подтверждено `git status`, `git rev-parse
|
||||
HEAD`). Изменилось только тело issue на GitHub — SHA репозитория тут не
|
||||
причём, доказательство ТЗ находится вне git.
|
||||
|
||||
## Скоуп ревью
|
||||
|
||||
Повторный заход ревью ТЗ (PROCESS.md §2.4, §2.9/§2.10) после жёлтого
|
||||
вердикта r2. Предмет — **дельта тела issue между r2 и r3**: одна правка,
|
||||
заявленная автором в комментарии `2026-09-10T15:33:29Z`
|
||||
(«Исправление по ревью ТЗ r2 → заход r3»): AC4 расширен на
|
||||
`firstStableRenderMs` изометрии, плюс отрицательный результат про
|
||||
`cache.entries.cleanFloor` изометрии перенесён из документа r2 в текст
|
||||
«Причины». Автор сам называет объём дельты: «одна строка AC4 и одна фраза
|
||||
в «Причине»» — проверено построчно, а не принято на слово (см. «Как
|
||||
проверялось»).
|
||||
|
||||
Дельта локальна: правка текста ТЗ, ни ребейза, ни смены контракта, ни
|
||||
новой подсистемы — код продукта между r2 и r3 не менялся вовсе (см.
|
||||
«Якоря»). Полный разбор всей задачи заново не требуется.
|
||||
|
||||
## Как проверялось
|
||||
|
||||
1. Получено текущее тело issue #520 (`gh issue view 520 --json body`) и
|
||||
комментарий автора после r2 (единственный новый комментарий, метка
|
||||
`S4-spec-review` возвращена).
|
||||
2. Прочитан `docs/reviews/SPEC-REVIEW-520-r2.md` целиком — вердикт, одна
|
||||
находка Medium-1, материал (SHA `ad2f9afd2bfe`, дерево `cc8e3bd73eeb`,
|
||||
тело `c5e79b63b531…`) — и `docs/reviews/SPEC-REVIEW-520-r1.md` для
|
||||
раздела «Унаследовано».
|
||||
3. **Находка Medium-1 r2 сверена построчно с текущим текстом**, а не
|
||||
принята на слово автора. r2 требовал: строка AC4 должна покрывать
|
||||
`firstStableRenderMs` изометрии тем же относительным пределом, что и
|
||||
`modelReadyMs`, — либо явно исключить её одной фразой. Текущая строка
|
||||
AC4 (тело issue, строка 59):
|
||||
> «`timing.firstStableRenderMs.median` профиля взаимодействия ≤ 3000
|
||||
> (абсолютный потолок) **и обе метрики старта — `modelReadyMs` и
|
||||
> `firstStableRenderMs` — внутри относительного лимита к базе
|
||||
> `a44fbd37` на обоих профилях, взаимодействия и изометрии**»,
|
||||
«чем краснеет»: «превышение любого из **четырёх** относительных
|
||||
пределов или абсолютного потолка — job красный».
|
||||
Это буквально включает `firstStableRenderMs` изометрии — предмет
|
||||
находки Medium-1. Закрытие полное, не частичное.
|
||||
4. **Пересчитаны все четыре относительных предела**, которые теперь
|
||||
называет AC4, по формуле `demo/performance/evaluate.mjs:73-118`
|
||||
(`relativeLimit = max(base*(1+ratio), base+allowance)`,
|
||||
`effective = min(hardMaxMs, relativeLimit)`), чтобы убедиться, что
|
||||
счёт «четырёх» верен и не сбит на единицу:
|
||||
- `modelReadyMs` interaction: база 1382, `budgets-large-house-
|
||||
interaction.json` (`maxRegressionRatio: 0.3, noiseAllowanceMs: 200,
|
||||
hardMaxMs: 2500`) → `max(1796.6, 1582) = 1796.6`; кандидат
|
||||
**1930 > 1796.6** — нарушен.
|
||||
- `firstStableRenderMs` interaction: база 2792.7, тот же файл
|
||||
(`ratio 0.3, allowance 250, hardMax 3000`) → `max(3630.5, 3042.7) =
|
||||
3630.5`, `effective = min(3000, 3630.5) = 3000` — здесь относительный
|
||||
предел поглощён абсолютным потолком, что и заявляет отдельная первая
|
||||
половина AC4; двойного счёта нет.
|
||||
- `modelReadyMs` isometric: база 2165.6, `budgets-large-house-
|
||||
isometric.json` (`ratio 0.2, allowance 200, hardMax 3000`) →
|
||||
`max(2598.72, 2365.6) = 2598.72`; кандидат **2626.3 > 2598.72** —
|
||||
нарушен (установлено ещё в r1).
|
||||
- `firstStableRenderMs` isometric: база 2227, тот же файл
|
||||
(`ratio 0.2, allowance 250, hardMax 3500`) → `max(2672.4, 2477) =
|
||||
2672.4`; кандидат **2678.2 > 2672.4** — нарушен (предмет находки
|
||||
Medium-1 r2, теперь покрыт текстом AC4).
|
||||
Итого ровно четыре относительных предела, все они действительно сейчас
|
||||
нарушены кандидатом — формулировка AC4 точна, не расширяет и не сужает
|
||||
набор относительно того, что показывает «Симптом».
|
||||
5. Проверено согласование «Контракта» (строка 46: «изометрия … чинится
|
||||
тем же изменением и проверяется теми же метриками старта (AC4)») с
|
||||
новым текстом AC4 — формулировка «тем же способом» из r2 заменена на
|
||||
«теми же метриками старта», что точнее отражает: AC4 теперь буквально
|
||||
покрывает обе метрики обоих профилей, а не «тот же способ» в общем
|
||||
виде. Расхождений между «Контрактом» и таблицей AC больше нет.
|
||||
6. Проверена вторая часть дельты — фраза в «Причине» (строка 24): «В
|
||||
бюджетах изометрии этот потолок не поднимался и уже равен 100 — там
|
||||
правка не нужна». Перепроверено чтением файлов (не принято на слово):
|
||||
`grep -n cleanFloor demo/performance/budgets-{isometric-smoke,large-
|
||||
house-isometric,interaction-smoke,large-house-interaction}.json` —
|
||||
оба файла изометрии дают `100`, оба файла взаимодействия — `180`.
|
||||
Совпадает с отрицательным результатом, который сам r2 получил
|
||||
самостоятельно (не то же самое, что просто скопировать вывод r2, —
|
||||
числа сверены заново на этом SHA, хоть код и не менялся).
|
||||
7. `git log --oneline ad2f9afd..b49721a0` и `git diff --stat` — единственный
|
||||
новый коммит это публикация документа r2; продуктовый код (`src/**`,
|
||||
`demo/**`, `scripts/**`, `test/**`) не менялся между r2 и r3 — гейты
|
||||
кода этому раунду не относятся (см. «Чего не проверял»), как и в r1/r2.
|
||||
8. Проверены state/labels issue (`OPEN`, `bug`, `P1`, `S4-spec-review`) —
|
||||
соответствуют ожиданиям заголовка задания (заход r3, 2/4 циклов).
|
||||
|
||||
## Находки
|
||||
|
||||
Нет находок, переживших проверку. Единственная находка r2 (Medium-1)
|
||||
закрыта полностью — см. «Закрытие раунда r2» и п.3–5 «Как проверялось».
|
||||
Новых находок делом дельты не выявлено: правка локальна (одна строка AC4,
|
||||
одна фраза «Причины»), обе проверены построчно и не вводят новых
|
||||
противоречий с остальным текстом ТЗ.
|
||||
|
||||
## Закрытие раунда r2
|
||||
|
||||
| Находка r2 | Чем закрыта | Где это видно |
|
||||
|---|---|---|
|
||||
| Medium-1 — амендированный AC4 не покрывал `firstStableRenderMs` изометрии (второе число того же предложения «Симптома», что и уже покрытый `modelReadyMs`) | AC4 переписан: «обе метрики старта — `modelReadyMs` и `firstStableRenderMs` — внутри относительного лимита… на обоих профилях»; «чем краснеет» — «любого из **четырёх** относительных пределов»; «Контракт» синхронизирован формулировкой «теми же метриками старта (AC4)» | Тело issue, AC4 (строка 59) и «Контракт» (строка 46). Численно перепроверено в п.4 «Как проверялось» — все четыре предела названы верно, счёт не сбит |
|
||||
|
||||
## Унаследовано из r2 (и, транзитивно, из r1)
|
||||
|
||||
Приняты без повторной проверки — дельта r2→r3 их не задевает, код между
|
||||
раундами не менялся:
|
||||
|
||||
- **Причина регрессии измерена, не догадка**: три технических утверждения
|
||||
о поведении `wrapped`-свойств Lit — r1 сверил дословно с исходником
|
||||
`@lit/reactive-element` (`docs/reviews/SPEC-REVIEW-520-r1.md`, «Как
|
||||
проверялось» п.4, материал SHA `27ea23c1ec25`, дерево `f80bcdd58b72`).
|
||||
- **M1 (раздел «Риски») и M2 (AC3) из r1** — закрыты в r2 и сверены там
|
||||
построчно (`docs/reviews/SPEC-REVIEW-520-r2.md`, «Как проверялось» п.3,
|
||||
«Закрытие раунда r1»), текст этих разделов между r2 и r3 не менялся.
|
||||
- **AC1** (source-scan тест по образцу `config-adoption-ownership.
|
||||
test.mjs`) и **AC2** (юнит на `onBodyReplaced`) — доказуемы дёшево и
|
||||
детерминированно, текст этих строк не менялся с r1 (сверено построчно
|
||||
с текущим телом в этом раунде — строки идентичны цитатам r1/r2).
|
||||
- **AC5** (`cache.entries.cleanFloor` ≤ 100 на двух interaction-файлах
|
||||
бюджетов) — привязан к точным файлам, не менялся; числа перепроверены
|
||||
заново в п.6 «Как проверялось» этого раунда (не просто унаследованы).
|
||||
- **Откат** (revert двух строк `static properties` + потолка 180) и
|
||||
**release-артефакты** (`User-Visible: no`, разблокирует
|
||||
`v1.74.0-beta.1`) — просты, однозначны, текст не менялся с r1.
|
||||
- **Продуктовая рамка** (администратор дома, поверхность — первый показ
|
||||
большого дома) совпадает с `docs/SCOPE.md` J1 и фикстурой 60 комнат/200
|
||||
устройств — r1 сверил, текст не менялся.
|
||||
- **Выбор полного трека** — верен, не оспаривается ни разу за три раунда.
|
||||
- **Ни одного вопроса владельцу не требуется** — весь материал технический
|
||||
(см. обоснование r1: «продуктовых развилок в задаче нет»), подтверждено
|
||||
в r1 и не оспорено в r2; делта r3 тоже чисто техническая (числа, счёт
|
||||
относительных пределов) — вывод не меняется.
|
||||
|
||||
Документы-источники: `docs/reviews/SPEC-REVIEW-520-r1.md` (материал —
|
||||
ветка `issue/520-first-frame` на `27ea23c1ec25`, дерево `f80bcdd58b72`),
|
||||
`docs/reviews/SPEC-REVIEW-520-r2.md` (материал — та же ветка на
|
||||
`ad2f9afd2bfe`, дерево `cc8e3bd73eeb`).
|
||||
|
||||
## Что проверено и корректно (этот раунд)
|
||||
|
||||
- Единственная правка фикс-комментария автора (`2026-09-10T15:33:29Z`)
|
||||
подтверждена чтением текущего тела issue, а не принята по заявлению —
|
||||
Medium-1 r2 закрыт полностью, не частично.
|
||||
- Счёт «четырёх относительных пределов» в AC4 арифметически точен:
|
||||
пересчитаны все четыре по опубликованной формуле и файлам бюджетов,
|
||||
все четыре сейчас действительно нарушены кандидатом — AC4 не занижает
|
||||
и не завышает то, что реально показывает «Симптом».
|
||||
- «Контракт» (строка 46) и таблица AC4 больше не расходятся в формулировке
|
||||
охвата изометрии — предыдущая асимметрия («тем же способом» против
|
||||
буквально одной метрики) устранена.
|
||||
- Фраза «Причины» про потолок `cache.entries.cleanFloor` изометрии
|
||||
(100, правка не нужна) подтверждена чтением всех четырёх файлов
|
||||
бюджетов заново на этом SHA, не просто перенесена из документа r2 на
|
||||
слово.
|
||||
- Код между r2 и r3 не менялся (`git log`, `git diff --stat` на
|
||||
диапазоне `ad2f9afd..b49721a0`) — дешёвые гейты кода (`tsc`, `test`,
|
||||
`build`) этому раунду не относятся по той же причине, что и в r1/r2:
|
||||
предмет этапа — текст ТЗ, продуктовой правки в дереве ещё нет.
|
||||
- Остальной текст ТЗ (унаследованное) не тронут дельтой — проверка
|
||||
инвариантов и код-ревью инфраструктуры (мутант, тесты) остаются
|
||||
предметом код-ревью после `S5-ready`.
|
||||
|
||||
## Чего не проверял
|
||||
|
||||
- **Гейты кода** (`tsc --noEmit`, `npm test`, `npm run build`,
|
||||
`check-docs.mjs`) — не гонял: `src/**` не менялся ни разу за три
|
||||
раунда, весь диапазон коммитов ветки — диагностика бенчмарка
|
||||
(`test(perf):`). Предмет этапа spec — текст ТЗ.
|
||||
- **`npm run invariants`** — не гонял: геометрия и её ссылки не тронуты
|
||||
ни кодом, ни правкой ТЗ.
|
||||
- **Реальный прогон `Full Performance`** — не запускал; числа «Симптома»
|
||||
и пересчитанные относительные пределы взяты из текста issue и
|
||||
файлов бюджетов в дереве, перепроверены арифметически по опубликованной
|
||||
формуле, не переизмерены заново на живом рантайме.
|
||||
- **Регистрацию мутанта `adoption-bodies-declared-reactive`** в
|
||||
`scripts/mutation-gate.mjs` — предмет код-ревью, ещё не существует
|
||||
(продуктовой правки AC1/AC2 в дереве ещё нет).
|
||||
- **Полную сверку разделов §7.1, не тронутых дельтой** (Риски, откат,
|
||||
release-артефакты, продуктовая рамка) — заново не перечитывал построчно,
|
||||
только сверил, что их текст байт-в-байт совпадает с тем, что видели r1
|
||||
и r2 (см. «Унаследовано»).
|
||||
|
||||
## Вердикт
|
||||
|
||||
Зелёный. High: 0, Medium: 0. ТЗ готово к разработке (`S5-ready`).
|
||||
Единственная находка r2 закрыта полностью и точно; новых находок дельта
|
||||
не вносит. Бюджет циклов §4 не расходуется (зелёный вердикт цикла не
|
||||
образует) и остаётся 2/4.
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/520-first-frame`, коммит `b49721a0bd98` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `50d8affd923b0a5e133ee1e453bb9388a5f44c78`
|
||||
```
|
||||
git log --all --format='%H %T' | grep 50d8affd923b
|
||||
```
|
||||
- Тело issue: `60492a3aec5ff7fed54f7b32c040b7a118526c209609d736ee68e116eb8f0aa6`
|
||||
- Вердикт конвейера: `green` · High 0
|
||||
Reference in New Issue
Block a user