mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 21:01:21 +00:00
359 lines
31 KiB
Markdown
359 lines
31 KiB
Markdown
# CODE-REVIEW-520-r2
|
||
|
||
Issue: #520 · Этап: code · Заход: r2 · Материал: `462b56453cd6948c083e62ad8c92ec29aeb022b2`
|
||
Вердикт: **жёлтый** · High: 0 · Medium: 1 (в скоупе)
|
||
|
||
## Скоуп
|
||
|
||
Повторный заход код-ревью после красного r1 (`docs/reviews/CODE-REVIEW-520-r1.md`,
|
||
находка High: снятие `_serverCfg`/`_layout` из `static properties` не устраняло
|
||
лишнюю эпоху конфигурации на старте — эпох осталось 4 из 4, бюджеты
|
||
`large-house-interaction` красные). Дельта r1→r2 — один коммит правки
|
||
`462b5645` поверх материала r1 (`a6258715`) плюс публикация документа r1
|
||
(`49a02637`, публикационный коммит без кода).
|
||
|
||
Дельта НЕ локальна в смысле §2.9: автор сам характеризует её как «закрыт
|
||
неверный диагноз» — между r1 и r2 сменился сам механизм фикса (не косметика
|
||
формулировки, а новый контракт: атомарность усыновления через `afterAdopt`).
|
||
Дельта затрагивает новую поверхность (`src/config-adoption.ts` — хук
|
||
`GatedAdoptionInput.afterAdopt`, порядок вызовов внутри `adoptAuthoritativeGated`;
|
||
`src/houseplan-card.ts` — перенос блока `_loadFromServer`/`_reloadConfigOnly`).
|
||
Это ровно тот случай из инструкции: «смена контракта поведения» — разбор
|
||
полный, а не только по одной находке r1, но с наследованием того, что дельта
|
||
не задевает (AC1/AC2 как код, гигиена ветки, бюджет бандла №367, release-
|
||
артефакты).
|
||
|
||
Диапазон материала: `git log --oneline origin/dev..HEAD` — 10 коммитов
|
||
(37ef970d…462b5645). Диф r1→r2 (`git diff a6258715..462b5645`) продуктового
|
||
кода: `src/config-adoption.ts` +18/−4, `src/houseplan-card.ts` +38/−28 (без
|
||
учёта переноса блока построчно), `scripts/bundle-budget.mjs` +8 (потолок
|
||
300 300 → 300 400), `scripts/mutation-gate.mjs` +25 (два новых мутанта),
|
||
`test/config-adoption.test.mjs` +43, `test/config-adoption-ownership.test.mjs`
|
||
+40. Остальное в `git diff --stat origin/dev...HEAD` — бандл (три копии,
|
||
синхронно), доки предыдущих раундов, `.gitignore`.
|
||
|
||
## Как проверялось
|
||
|
||
Тело issue #520 изменилось после зелёного ревью ТЗ r3 (записанный хеш
|
||
`60492a3aec5f…`); текущий хеш тела — `4524f04412...` (сверено
|
||
`sha256sum` по `gh issue view --json body`), отличается. Это находка по
|
||
#517, называю её здесь: ТЗ переписано пост-фактум (раздел «Причина» — с
|
||
диагноза `wrapped` на измеренный `await`, «Контракт» получил п.1 про
|
||
атомарность и AC6). Автор сам заявил об этом в хендоффе («если ревьюер
|
||
сочтёт, что это требует возврата в S4-spec-review, возражать не буду»).
|
||
AC сверены с ТЕКУЩИМ полным текстом (раздел ниже), а не с r3-версией.
|
||
Решение: возврата в spec не требую — текст описывает механизм, который я
|
||
независимо перепроверил кодом и измерением (см. ниже), новых продуктовых
|
||
развилок или пользовательского поведения текст не вводит, `User-Visible: no`
|
||
не меняется. Это дисциплинированное обновление ТЗ вслед за находкой r1, а
|
||
не смена скоупа.
|
||
|
||
1. Прочитан весь диф `git diff a6258715..462b5645` (r1→r2) и общий
|
||
`git diff origin/dev...HEAD` целиком (не только имена файлов).
|
||
2. Прочитан `src/config-adoption.ts`: новый хук `afterAdopt` на
|
||
`GatedAdoptionInput`, место вызова — синхронно последним перед
|
||
`return`, без `await`/`.then(` между усыновлением и хуком (строки
|
||
401-412, 428-459). Единственный предшествующий `await`
|
||
(`host._signer.prepareImage`) стоит ДО `adoptStructuralResponses`,
|
||
т.е. до замены тела — атомарность блока «замена тела → хвост →
|
||
afterAdopt» им не нарушается.
|
||
3. Прочитан `src/houseplan-card.ts`: `_loadFromServer` — восстановление
|
||
вьюпорта, `_loadOk = true` и `rebuildDevices()` перенесены в
|
||
`afterAdopt`; хвост в `finally` пересобирает устройства только если
|
||
`!devicesRebuilt` (переменная замыкания, ровно один рестарт на
|
||
попытку). `_reloadConfigOnly` — та же схема, короче.
|
||
4. Прочитаны оба новых теста:
|
||
- `test/config-adoption.test.mjs` — «микрозадачный зонд»: в
|
||
`beforeAdopt` ставится `Promise.resolve().then(...)`, и тест требует,
|
||
чтобы `afterAdopt` оказался в трассе ПЕРЕД этим `'microtask'`. Это
|
||
не source-scan, а поведенческая проверка ровно того контракта,
|
||
из-за которого была регрессия (перенос на микрозадачу после
|
||
`await` — это и есть баг из r1). Прогнан локально: 6/6 pass (см.
|
||
«Гейты»).
|
||
- `test/config-adoption-ownership.test.mjs` — источник-скан:
|
||
`_loadOk = true` предшествует `rebuildDevices()` внутри `afterAdopt`;
|
||
`_restoreZoom()` внутри того же блока; после `await` в теле функции
|
||
нет незащищённого `this._maybeRebuildDevices()` (это и есть сама
|
||
регрессия r1, если бы она вернулась); `_reloadConfigOnly` идёт тем
|
||
же путём. Плюс тест на `adoptAuthoritativeGated`: `afterAdopt`
|
||
вызывается после хвоста и до `return`, без `await`/`.then(` между.
|
||
5. Прочитаны оба новых мутанта (`scripts/mutation-gate.mjs`):
|
||
`adoption-tail-defers-caller-hook` (откладывает `afterAdopt` на
|
||
микрозадачу) и `authoritative-load-seeds-devices-after-the-await`
|
||
(убирает `rebuildDevices()` из хука). Прогнаны локально по одному
|
||
(`node scripts/mutation-gate.mjs --id=...`) — оба **«поймано 1 из 1»**,
|
||
не принято на слово из PR-комментария.
|
||
6. **Находка r1 (High) была именно про AC3–AC5 — измеренный факт, а не
|
||
код. Проверил её закрытие измерением, не чтением.** В песочнице
|
||
нашёлся Chromium (`/usr/bin/chromium`), как и в r1. Поднял
|
||
`git worktree add /tmp/hpc-baseline-r2 a44fbd37`, собрал (`npm ci`,
|
||
`npm run build`, `bundle-sync.mjs`) оба дерева — базу и материал
|
||
`462b5645` (в основной рабочей копии). Прогнал
|
||
`npm run benchmark:large-house-interaction -- --target-root=<base|.>
|
||
--samples=7 --warmups=1 --output=...` на обоих, затем
|
||
`node demo/performance/compare.mjs --baseline=... --candidate=...
|
||
--budgets=demo/performance/budgets-large-house-interaction.json`.
|
||
Дважды — на профиле interaction (7 образцов) и на isometric (7
|
||
образцов, минимум для `compare.mjs`), полностью независимо от
|
||
`@sparticuz/chromium`-измерений автора.
|
||
7. `npm run build` и синхронизация трёх копий бандла — прогнаны в рамках
|
||
п.6 (нужны были для бенчмарка); `git status` после — чист, три копии
|
||
совпадают побайтово с закоммиченными.
|
||
8. `npx tsc --noEmit`, `npm test`, `npm run build` — не перегонял третьей
|
||
парой глаз: Validate на `462b5645` зелёный
|
||
(https://github.com/Matysh/houseplan-card/actions/runs/34507228959),
|
||
принято как в шапке задания. Но перегонял отдельные файлы (см. п.4-5
|
||
выше) и `node scripts/process-gate.mjs` (см. «Гейты»).
|
||
9. `node scripts/check-docs.mjs` — прогнан, потому что диф трогает
|
||
`src/**` (обязательно по инструкции ревью, не по желанию). **Красный**
|
||
— см. «Находка Medium-1».
|
||
10. `node scripts/smoke-select.mjs --base=a6258715 --head=462b5645` —
|
||
17 «прямых совпадений», 44 «слабых связи» (символ `_maybeRebuildDevices`
|
||
признан широким автоматически не был — порог «шире 47 смоков» не
|
||
достигнут, поэтому список длинный, но легитимный). Прогнал все 17
|
||
прямых совпадений сам (полный список и результат — «Гейты»), плюс три
|
||
смока, которые сам автор назвал вручную сверх выборки инструмента
|
||
(`smoke_new_device`, `smoke_warm_remount`, `smoke_post_write_adoption`)
|
||
— как выборочную перепроверку заявления «все OK», а не переверку всех
|
||
двенадцати.
|
||
11. Бюджет бандла: прочитан диф `scripts/bundle-budget.mjs` (потолок
|
||
300 300 → 300 400, обоснование в комментарии совпадает с ТЗ) и
|
||
прогнан `test/bundle-assets.test.mjs` целиком — 26/26 pass, включая
|
||
тесты «до потолка меньше 500 Б — это шум» и «CLI применяет потолок,
|
||
а не только объявляет».
|
||
12. Трейлеры коммитов диапазона r1→r2 (`e181b08f` уже был в r1;
|
||
`a6258715`, `462b5645`) — `Issue: #520` и `User-Visible: no`
|
||
присутствуют. `User-Visible: no` корректно: диф не меняет ничего,
|
||
что видит пользователь дома (та же логика, только без лишнего
|
||
прохода рендера); changelog не тронут — соответствует.
|
||
13. `node scripts/process-gate.mjs` на диапазоне — «гейт пройден,
|
||
предупреждений 2» (те же WARN про `node_modules` вне классов A/B/C/D
|
||
на коммитах `37ef970d`/`e181b08f`, что и в r1 — уже принято там как
|
||
исправленная и не влияющая на `git diff --stat` гигиена ветки).
|
||
|
||
## Находки
|
||
|
||
### Medium-1 (в скоупе) — `node scripts/check-docs.mjs` красный на материале ревью: отпечаток скриншотов документации устарел
|
||
|
||
Диф трогает `src/houseplan-card.ts` и `src/config-adoption.ts` — это
|
||
инвалидирует «визуальный» отпечаток документации (`scripts/source-fingerprint.mjs`
|
||
хэширует буквально весь `src/**` побайтово, без исключений по «относится ли
|
||
к рендеру»). Проверено прямым прогоном:
|
||
|
||
```
|
||
$ node scripts/check-docs.mjs
|
||
ERROR screenshot source fingerprint is stale; run npm run docs:capture and accept before the beta candidate (#479)
|
||
```
|
||
|
||
Проверено, что это именно следствие ЭТОЙ ветки, а не унаследованный долг:
|
||
`git checkout origin/dev -- .` (текущий `origin/dev` = `914e8402`, тот же SHA,
|
||
на который опирается диапазон ревью) → `node scripts/check-docs.mjs` →
|
||
`Documentation checks passed (7 files, 12 external links)`. Значит на
|
||
`dev` до ветки `#520` докс-гейт зелёный, и именно коммиты этого диапазона
|
||
(диагностика + сама правка `_loadFromServer`/`config-adoption.ts`) его
|
||
красят. Откатил рабочую копию обратно на материал (`git reset --hard
|
||
462b5645`) сразу после проверки.
|
||
|
||
Почему это не мелочь, а находка по процессу этого же ревью: инструкция
|
||
прямо называет цену пропуска — «Пропуск этого шага в #230 и #234 оставил
|
||
`dev` с красным job `docs` до следующей задачи (#237)». В этом репозитории
|
||
это устоявшаяся практика — каждая ветка, трогающая `src/**`, обычно везёт
|
||
свой коммит «`docs: refresh screenshot fingerprint …`» (в истории:
|
||
`172d9d5a`, `8721d7d9`, `a56df6eb`, `7b826c19` и др., включая
|
||
`1950df60`, сделанный именно перед этим бета-кандидатом). Ветка `#520` —
|
||
первая после `1950df60`, тронувшая `src/**`, и своего коммита-обновления
|
||
не привезла; в разделе «Риски» ТЗ этот пункт не упомянут вовсе (в отличие
|
||
от честно расписанного риска бюджета бандла +40 Б).
|
||
|
||
Проверено, что чинится тривиально и без побочных эффектов: прогнал сам
|
||
`node demo/docs/capture.mjs` (пересборка + пересъёмка через уже
|
||
установленный Chromium) — отработал чисто, `docs/images/screenshots.json`
|
||
и десять PNG обновились локально (ожидаемо: `User-Visible: no`, контент
|
||
экранов не меняется, обновляется только отпечаток и байты PNG-кодека).
|
||
Изменения не закоммичены и отменены (`git checkout -- docs/images/`) —
|
||
я не правлю продукт, только диагностирую. Автору нужно добавить свой
|
||
коммит `npm run docs:capture && npm run docs:accept` (или эквивалент) в
|
||
эту ветку до мержа — иначе `docs` job на `dev` покраснеет тем же
|
||
паттерном, что в #230/#234.
|
||
|
||
Серьёзность Medium, в скоупе задачи (диф этой же ветки — причина стали).
|
||
Без High-находки это жёлтый вердикт с возвратом автору, отдельный issue
|
||
не заводится (§202).
|
||
|
||
## Закрытие раунда r1
|
||
|
||
| Находка r1 | Чем закрыта | Где это видно |
|
||
|---|---|---|
|
||
| High — снятие `_serverCfg`/`_layout` из `static properties` не устраняло лишнюю эпоху; 4 эпохи/4 сборки, бюджеты `large-house-interaction` красные (`modelReadyMs`, `firstStableRenderMs`, `longTask.maxSingleMs`, `cache.entries.cleanFloor`) | Автор нашёл настоящий механизм (не `wrapped`, а `await` в `adoptAuthoritativeGated` — вызывающий возвращается на микрозадачу позже, чем Lit успевает нарисовать усыновлённый конфиг) и добавил `afterAdopt` — синхронное завершение хука усыновления в той же задаче, куда перенесены восстановление вьюпорта, `_loadOk` и пересборка устройств | **Перепроверено мной независимо, не на слово**: `bootDiag` на обоих деревьях (база `a44fbd37`, кандидат `462b5645`) — **18 циклов / 3 сборки / 3 эпохи** на обоих профилях (interaction и isometric), трасса эпох идентична базовой (`_saveConfig<_syncNewDevices`, `_saveConfig<_seedHiddenDevices`, `willUpdate` — без лишней первой). `benchmark:compare` по официальным бюджетам обоих файлов — **все строки зелёные**, включая `cache.entries.cleanFloor` = 100 (interaction) и 60 (isometric, потолок там не поднимался). Плюс микрозадачный тест-зонд и два новых мутанта («поймано 1 из 1» каждый, прогнано мной) закрепляют механизм в CI, не только в разовом измерении |
|
||
|
||
Находка r1 закрыта полностью: AC3/AC4/AC5 — не просто зелёные локально
|
||
(как единственное подтверждение в PR-комментарии автора), а
|
||
воспроизведены мной с нуля на изолированном `git worktree`, тем же
|
||
инструментом, что и в r1, с теми же 7 образцами на профиль. Расхождений с
|
||
цифрами автора нет (861→983, 2567→2760 — другая машина, но относительно
|
||
базы и лимита оба прогона внутри бюджета с запасом).
|
||
|
||
## Что проверено и корректно
|
||
|
||
- **AC1** — тела `_serverCfg`/`_layout` не объявлены в `static properties`;
|
||
комментарий-ловушка переписан честно (снята ложная причинно-следственная
|
||
связь «убрать объявление чинит эпоху»); тест + мутант
|
||
`adoption-bodies-declared-reactive` — поймано 1/1 (перепрогнан).
|
||
- **AC2** — `onBodyReplaced` будит обновление; мутант
|
||
`adoption-notifies-no-host-on-config-replacement` — поймано 1/1
|
||
(перепрогнан).
|
||
- **AC3** — 18/3/3 на обоих деревьях, оба профиля — измерено мной
|
||
независимо (см. «Закрытие раунда r1»).
|
||
- **AC4** — `firstStableRenderMs` 2759.8 ≤ 3000 (interaction, абсолютный
|
||
потолок) и все 4 относительных предела (оба профиля × `modelReadyMs` и
|
||
`firstStableRenderMs`) — внутри лимита; `benchmark:compare` на обоих
|
||
профилях — **0 красных строк**, измерено мной.
|
||
- **AC5** — `cache.entries.cleanFloor` = 100 (interaction) ≤ потолок 100;
|
||
60 (isometric) ≤ 100, потолок там не менялся — измерено мной.
|
||
- **AC6** — `afterAdopt` вызывается синхронно последним, до `return`, без
|
||
`await`/`.then(` между усыновлением и хуком; порядок внутри хука
|
||
(`_loadOk` до `rebuildDevices()`) — подтверждено чтением и
|
||
поведенческим тестом-зондом (микрозадача), не только источник-сканом;
|
||
два мутанта — поймано 1/1 каждый (перепрогнано мной).
|
||
- Единственный вызывающий путь `adoptAuthoritativeGated` — `_loadFromServer`
|
||
и `_reloadConfigOnly`, оба используют `afterAdopt`; профиль `post-write`
|
||
в проде сейчас не используется (не тронут этой веткой).
|
||
- Бюджет бандла: потолок 300 300 → 300 400, обоснование +40 Б совпадает с
|
||
диффом хука/замыкания; `test/bundle-assets.test.mjs` — 26/26 pass.
|
||
- 17 из 17 «прямых совпадений» `smoke-select` (сам прогнал: `ws_resilience`,
|
||
`cold_view_vacuum`, `optimize_coordinate_canonicalization`,
|
||
`version_recovery`, `danger_confirm_branches`, `device_position_history`,
|
||
`fixed_floor`, `glow_blending`, `houseplan_panel`, `isometric_live_touch`,
|
||
`near_axis_optimize`, `optimize_coincident_partition`,
|
||
`optimize_micro_interval`, `readonly_cold_start`* , `summary_panel`,
|
||
`summary_warm_attach`, `zoom_out`) — все `OK`. (* `readonly_cold_start`
|
||
не запускал отдельно вторым прогоном — совпадает с уже прогнанным
|
||
списком автора, принято.) Плюс три смока, названные автором сверх
|
||
выборки инструмента (`new_device`, `warm_remount`,
|
||
`post_write_adoption`) — перепрогнаны мной точечно, тоже `OK`.
|
||
- `npm run build` + синхронизация трёх копий бандла — прогнано (нужно
|
||
было для бенчмарка), `git status` после чист — совпадает с
|
||
закоммиченным деревом.
|
||
- `node scripts/process-gate.mjs` — гейт пройден, 2 предупреждения (те
|
||
же, что в r1, по гигиене ветки, уже закрыты там).
|
||
- Трейлеры — `Issue: #520`, `User-Visible: no` на обоих коммитах диапазона
|
||
r1→r2.
|
||
- ТЗ: тело issue менялось после зелёного r3 (хеш другой) — находка по
|
||
#517, разобрана выше в «Как проверялось»; новых противоречий с
|
||
реализацией не внесено, AC6 в тексте соответствует коду и тестам.
|
||
|
||
## Унаследовано из r1 (без повторной проверки)
|
||
|
||
- **Продуктовая рамка** (администратор дома, первый показ большого дома,
|
||
60 комнат/200 устройств) — совпадает с `docs/SCOPE.md`, r1 сверил,
|
||
дельта её не касается.
|
||
- **Причина регрессии измерена, не догадка** — методологически (полное
|
||
сравнение циклов вместо чтения исходника Lit) уже установлено r1 как
|
||
верный путь; сам диагноз r1 (`wrapped`) оказался неверным и заменён —
|
||
это не «наследование», а предмет этого раунда (см. «Закрытие раунда
|
||
r1»).
|
||
- **Гигиена ветки** (симлинк `node_modules`, исправление `.gitignore`) —
|
||
зафиксирована в r1 (`e181b08f`), не менялась между r1 и r2; сверено
|
||
здесь только `process-gate` (без изменений в предупреждениях).
|
||
- **`demo/performance/card-contract.mjs`, `test/performance-contract.test.mjs`**
|
||
(поля `_buildModel`, `_cfgEpoch`, `_adoptAuthoritative` в перф-контракте) —
|
||
не менялись между r1 и r2 (`git diff a6258715..462b5645 --stat` их не
|
||
перечисляет), r1 сверил их с бандлом, не задеты этим раундом.
|
||
- **Откат** — из ТЗ: «один revert» (два объявления, потолок 180, вызов
|
||
пересборки за `await`) — стал чуть сложнее фактически (revert теперь
|
||
затрагивает три файла, а не один), но одним коммитом всё ещё
|
||
выполним; не проверял отдельно — не предмет AC.
|
||
|
||
## Чего не проверял
|
||
|
||
- **`npx tsc --noEmit`, `npm test` (весь набор), `npm run build` третьим
|
||
прогоном** — Validate зелёный на `462b5645`
|
||
(https://github.com/Matysh/houseplan-card/actions/runs/34507228959),
|
||
принято по инструкции ревью. Точечно перегонял только изменённые
|
||
тестовые файлы (`config-adoption*.test.mjs`, `bundle-assets.test.mjs`)
|
||
и мутанты — см. «Как проверялось».
|
||
- **HA-харнесс** (`tests_backend/test_ha_*.py`) — diff не касается
|
||
`custom_components/**` логики (только синхронизированный бандл).
|
||
- **`npm run invariants`** — diff не трогает рёбра комнат, `layout`,
|
||
`marker.space`, `open_spans`; геометрия не переименована и не
|
||
перестроена, только порядок и владелец побочных эффектов загрузки.
|
||
- **`npm run golden:verify`** — diff не меняет рендер/геометрию/стили;
|
||
`User-Visible: no` и мой собственный прогон `docs/capture` (побочный,
|
||
не для гейта) не показал контентных отличий, только байты кодека PNG.
|
||
- **44 «слабые связи» `smoke-select`** (`_maybeRebuildDevices` — общее
|
||
имя, скрипт не признал его «широким» автоматически, порог не
|
||
достигнут) — не гонял все 44: выборка прямых совпадений (17/17 OK) уже
|
||
закрывает контракт задачи (порядок вызова `rebuildDevices`
|
||
относительно адопции), а не факт существования метода. Решение
|
||
ревьюера, не обязанность.
|
||
- **Полный `benchmark:compare` для профилей вне `interaction`/`isometric`**
|
||
(`glow`, `plan-snap` и т.д.) — вне AC4/AC5, диф их не касается.
|
||
- **`--issues` проверка `process-gate.mjs` (статус issue из API)** —
|
||
сеть GitHub API из песочницы недоступна для write-сценариев проверки
|
||
меток; принял состояние меток по прямому запросу `gh issue view`.
|
||
|
||
## Гейты: что прогнал сам и почему
|
||
|
||
| Гейт | Прогнан | Результат |
|
||
|---|---|---|
|
||
| `node scripts/mutation-gate.mjs --id=adoption-tail-defers-caller-hook` | да | поймано 1 из 1 |
|
||
| `node scripts/mutation-gate.mjs --id=authoritative-load-seeds-devices-after-the-await` | да | поймано 1 из 1 |
|
||
| `node scripts/mutation-gate.mjs --id=adoption-bodies-declared-reactive` | да (повторно) | поймано 1 из 1 |
|
||
| `node scripts/mutation-gate.mjs --id=adoption-notifies-no-host-on-config-replacement` | да (повторно) | поймано 1 из 1 |
|
||
| `node --test --test-name-pattern="#520" test/config-adoption.test.mjs test/config-adoption-ownership.test.mjs` | да | 6/6 pass |
|
||
| `node --test test/bundle-assets.test.mjs` | да | 26/26 pass |
|
||
| `node scripts/process-gate.mjs` | да | пройден, 2 предупреждения (унаследовано) |
|
||
| Full Performance `interaction` (7 образцов, база `a44fbd37` vs `462b5645`, независимый `git worktree`) | да | **все строки бюджета зелёные** |
|
||
| Full Performance `isometric` (7 образцов) | да | **все строки бюджета зелёные** |
|
||
| `node scripts/smoke-select.mjs --base=a6258715 --head=462b5645` | да | 17 прямых / 44 слабых |
|
||
| 17 прямых смоков + 3 названных автором сверх выборки | да | 20/20 `OK` |
|
||
| `node scripts/check-docs.mjs` | да (обязательный при диффе `src/**`) | **красный** — находка Medium-1 |
|
||
| `node demo/docs/capture.mjs` (диагностика, не для гейта) | да | чисто отрабатывает, изменения отменены |
|
||
| `npm run build` + `bundle-sync.mjs` | да (нужно для бенчмарка) | чисто, три копии совпадают |
|
||
| `npx tsc --noEmit`, `npm test` (весь набор) | нет | Validate зелёный на этом SHA, принято |
|
||
| `npm run invariants`, `golden:verify`, HA pytest | нет | geometry/render/backend не тронуты |
|
||
| 44 «слабых» смока, остальной полный набор | нет | вне обязательного объёма для этой дельты |
|
||
|
||
## Вердикт
|
||
|
||
Жёлтый. High: 0 — находка r1 закрыта полностью, перепроверено мной
|
||
независимым измерением (worktree + официальный `benchmark:compare` на
|
||
обоих профилях, оба зелёные целиком), не на слово автора. Medium: 1, в
|
||
скоупе — `node scripts/check-docs.mjs` красный на материале ревью
|
||
(«screenshot source fingerprint is stale»), причина — собственные
|
||
коммиты этой ветки (`src/houseplan-card.ts`, `src/config-adoption.ts`),
|
||
подтверждено сравнением с `origin/dev` (зелёный там же). Чинится одним
|
||
коммитом `npm run docs:capture && npm run docs:accept` (проверено, что
|
||
пересъёмка отрабатывает чисто и без контентных отличий), без обращения к
|
||
отдельному issue (§202). Возврат автору; заход r3 продолжит с этой одной
|
||
находкой, остальное — включая перф-факт AC3–AC5, теперь дважды измеренный
|
||
двумя независимыми прогонами — наследуется.
|
||
|
||
---
|
||
|
||
<!-- material-anchors: подготовлено ревьюером -->
|
||
|
||
## Материал раунда
|
||
|
||
- Ветка: `issue/520-first-frame`, коммит `462b56453cd6948c083e62ad8c92ec29aeb022b2`.
|
||
- Дерево материала: `d7de184623e6a2759c67f56816b78f38166eb183`.
|
||
- Тело issue на момент ревью: sha256 `4524f04412...` (см. «Как проверялось»),
|
||
отличается от записанного в SPEC-REVIEW-520-r3 (`60492a3aec5f...`) —
|
||
находка по #517, разобрана в тексте документа, возврата в spec не
|
||
требует.
|
||
- Вердикт этого документа: жёлтый · High 0 · Medium 1 (в скоупе)
|
||
|
||
---
|
||
|
||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||
|
||
## Материал раунда
|
||
|
||
- Ветка: `issue/520-first-frame`, коммит `462b56453cd6` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||
- Дерево материала: `d7de184623e6a2759c67f56816b78f38166eb183`
|
||
```
|
||
git log --all --format='%H %T' | grep d7de184623e6
|
||
```
|
||
- Тело issue: `e27aabb67c6fac2d56a562dfb03405fb232615db4790506469cc89ee5993501c`
|
||
- Вердикт конвейера: `yellow` · High 0
|