# CODE-REVIEW-520-r3 Issue: #520 · Этап: code · Заход: r3 · Материал: `a325294d7a9d8ef4ecfeab2ed3e248318a650ab5` Вердикт: **зелёный** · High: 0 · Medium: 0 ## Скоуп Третий заход код-ревью после жёлтого r2 (`docs/reviews/CODE-REVIEW-520-r2.md`, единственная находка Medium-1: `node scripts/check-docs.mjs` красный на материале ревью — диф трогает `src/houseplan-card.ts` и `src/config-adoption.ts`, что инвалидирует отпечаток скриншотов документации, а ветка не привезла свой коммит обновления). Дельта r2→r3 — ровно один коммит правки `a325294d` поверх материала r2 (`462b5645`) плюс публикационный коммит документа r2 (`3066fec1`, без кода): ``` $ git diff --stat 462b5645..a325294d docs/images/screenshots.json | 24 +-- docs/reviews/CODE-REVIEW-520-r2.md | 358 +++++++++ 2 files changed, 370 insertions(+), 12 deletions(-) ``` Дельта **локальна** в смысле §2.9/§2.10: только `docs/images/screenshots.json` (класс C, отпечаток + 11 `sourceSha256`), ни одного файла класса A/B, `dist/**` и `custom_components/houseplan/frontend/**` не тронуты, продуктовый код между r2 и r3 не менялся вовсе (`git diff 462b5645..a325294d -- src test scripts custom_components` — пусто). Разбор в этом раунде сужен до дельты и до находки r2 — остальное наследуется из r2 без повторной проверки (раздел ниже). ## Как проверялось 1. Найден вердикт предыдущего раунда (комментарий issue #11, 2026-09-10 17:47:34) и материал, на котором он получен: `462b56453cd6948c083e62ad8c92ec29aeb022b2`, зафиксирован в `docs/reviews/CODE-REVIEW-520-r2.md` блоком «Материал раунда». `git diff 462b5645..a325294d` — предмет этого раунда (см. «Скоуп»). 2. По единственной находке r2 показано, чем именно она закрыта — не заявлением автора: `git diff 462b5645..a325294d -- docs/images/screenshots.json` меняет ровно `sourceFingerprint` и 11 полей `sourceSha256` (`ccbbfb4e5aa1… → c615d58061456…`), ни один `imageSha256` не изменился — то есть ни один PNG не поменялся, менялся только отпечаток источников. Прогнал сам: ``` $ node scripts/check-docs.mjs Documentation checks passed (7 files, 12 external links). ``` Красный до фикса зафиксирован в документе r2 буквальным выводом той же команды («ERROR screenshot source fingerprint is stale…») — «умеет падать» доказано предыдущим раундом, здесь показан переход в зелёное на исправленном материале. 3. Трейлеры коммита `a325294d`: `Issue: #520`, `User-Visible: no` — корректно, диф не меняет ничего, что видит пользователь дома (только служебный отпечаток документации); changelog не тронут — соответствует. 4. `dist/**` и `custom_components/houseplan/frontend/**` не входят в диф этого коммита (`git show --stat a325294d`) — ожидаемо для чисто документационной правки; сверил, что три копии бандла всё ещё совпадают побайтово: `cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` — молча (совпадают). 5. `node scripts/process-gate.mjs` на диапазоне `origin/dev..HEAD` — «гейт пройден, предупреждений 2» (те же WARN про `node_modules` вне классов A/B/C/D на исторических коммитах `37ef970d`/`e181b08f`, уже закрыты и разобраны в r1/r2 как гигиена ветки, не влияющая на финальное дерево). 6. Дешёвые гейты (`typecheck`, `npm test`, `npm run build` со сверкой копий бандла) на точном материале `a325294d` подтверждены зелёным Validate: https://github.com/Matysh/houseplan-card/actions/runs/34513504274 (`headSha` сверен через `gh run view --json headSha` — совпадает с материалом раунда). Не перегонял третьей парой глаз — принято по инструкции ревью, это не новый код с прошлого раунда (продуктовый код между r2 и r3 не менялся). 7. **Тело issue менялось повторно после r2.** Ревью r2 уже зафиксировало расхождение с записанным хешем SPEC-REVIEW-520-r3 (#517: `60492a3aec5f…` против тогдашнего `4524f04412...`). Между вердиктом r2 (17:47:34) и материалом r3 тело изменилось **ещё раз** — автор сам называет это в хендоффе (комментарий issue #12): добавлены AC7 и пункт риска про отпечаток скриншотов, по прямому требованию находки Medium-1 r2. Текущий хеш тела: `sha256sum` по тексту, полученному `gh issue view 520 --json body -q .body` → `f318b25f5815fa66aeaa7cbc5ce1d63e23fab0a8e50493962634b01e0ca86e96` — отличается от обоих предыдущих записанных значений. Сверено построчно: правка аддитивна — AC1–AC6, «Причина», «Контракт», «Риски» (кроме одного нового пункта) не тронуты; новый AC7 («`check-docs` зелёный на материале ревью») и новый риск-пункт об отпечатке скриншотов дословно описывают именно то, что закрывает коммит `a325294d`, и ничего сверх этого. Новых продуктовых развилок текст не вводит, `User-Visible: no` не меняется. Возврата в `S4-spec-review` не требую — та же логика, что применил r2 к первому расхождению (дисциплинированное обновление ТЗ вслед за находкой кода, не смена скоупа). ## Закрытие раунда r2 | Находка r2 | Чем закрыта | Где это видно | |---|---|---| | Medium-1 — `node scripts/check-docs.mjs` красный на материале ревью: отпечаток скриншотов документации устарел (диф трогает `src/houseplan-card.ts`, `src/config-adoption.ts`) | Коммит `a325294d`: отпечаток обновлён через `npm run docs:accept -- --identical` (#512) — все 11 кадров пересняты и совпали с закоммиченными побайтово (ни один `imageSha256` не изменился), обновился только `sourceFingerprint` и 11 `sourceSha256` | Прогнал `node scripts/check-docs.mjs` на материале `a325294d` сам — «Documentation checks passed (7 files, 12 external links)»; `git diff 462b5645..a325294d -- docs/images/screenshots.json` показывает ровно 12 изменённых полей, ноль изменённых `imageSha256` | Находка r2 закрыта полностью и минимально — правка не расширилась за пределы её предмета. ## Унаследовано из r2 (без повторной проверки) Продуктовый код между материалом r2 (`462b5645`) и материалом r3 (`a325294d`) не менялся — дельта чисто документационная (см. «Скоуп»), поэтому из `docs/reviews/CODE-REVIEW-520-r2.md` наследуется всё содержательное: - **Находка r1 (High) и её закрытие.** Механизм регрессии (микрозадача после `await` в `adoptAuthoritativeGated`, а не Lit `wrapped`) и хук `afterAdopt` — r2 перепроверил независимым измерением на изолированном `git worktree` (база `a44fbd37` vs `462b5645`), оба профиля (`interaction`, `isometric`), `benchmark:compare` — все строки бюджета зелёные. Код с тех пор не менялся, повторное измерение в этом раунде избыточно. - **AC1, AC2, AC6** — тесты и все четыре мутанта (`adoption-bodies-declared-reactive`, `adoption-notifies-no-host-on-config-replacement`, `adoption-tail-defers-caller-hook`, `authoritative-load-seeds-devices-after-the-await`) перепрогнаны r2 лично, «поймано 1 из 1» каждый; код, которого это касается, в дельте r2→r3 отсутствует. - **AC3, AC4, AC5** — измерены r2 независимо (не на слово автора): 18 циклов / 3 сборки / 3 эпохи на обоих деревьях и обоих профилях, `benchmark:compare` — 0 красных строк, `cache.entries.cleanFloor` 100 (interaction) / 60 (isometric). Перф-путь этой дельтой не задет. - **Бюджет бандла** (потолок 300 300 → 300 400, +40 Б) и `test/bundle-assets.test.mjs` (26/26 pass) — не тронуты дельтой. - **17 из 17 «прямых совпадений» `smoke-select`** плюс 3 названных автором сверх выборки — прогнаны r2 лично, все `OK`; исходный код, который они проверяют, в этой дельте не менялся. - **Гигиена ветки** (симлинк `node_modules`, исправление `.gitignore`) — зафиксирована в r1, не менялась. - **Первое расхождение тела issue с хешем SPEC-REVIEW-520-r3** (#517) — разобрано r2, решение «возврата в spec не требую» подтверждено тем же рассуждением и в этом раунде (см. п.7 «Как проверялось» — уже про второе, новое расхождение). - **Откат** — «один revert» из ТЗ технически устарел (теперь три файла, не один), это отмечено уже r2 как факт вне AC; не предмет этого раунда. Материал: `docs/reviews/CODE-REVIEW-520-r2.md`, SHA `462b56453cd6948c083e62ad8c92ec29aeb022b2`. ## Что проверено и корректно - **AC7** (новый в теле issue, добавлен вслед за находкой Medium-1 r2) — `node scripts/check-docs.mjs` зелёный на материале `a325294d`, прогнан лично: «Documentation checks passed (7 files, 12 external links)». «Чем краснеет» — устаревший `sourceFingerprint`, продемонстрировано самим переходом красный (документ r2) → зелёный (этот документ) на одном и том же коде отпечатка. - Ни один `imageSha256` не изменился в диффе r2→r3 — визуальный контент скриншотов не менялся, обновление чисто механическое (отпечаток источников). - Три копии бандла (`dist/houseplan-card.js`, `custom_components/houseplan/frontend/houseplan-card.js`, стендовая не коммитится) совпадают побайтово — сверено `cmp`. - Трейлеры `a325294d` — `Issue: #520`, `User-Visible: no`, корректно для чисто документационной правки. - `node scripts/process-gate.mjs` — гейт пройден, предупреждения те же, что и в r1/r2 (гигиена исторических коммитов, не влияет на финальное дерево). - Validate зелёный на точном материале раунда (`headSha` сверен явно, не принят на слово) — typecheck/test/build подтверждены этим прогоном. - Второе расхождение тела issue с записанным хешем ревью ТЗ — названо, разобрано построчно (аддитивно, соответствует коммиту `a325294d`), решение «без возврата в spec» обосновано. ## Чего не проверял - **`npx tsc --noEmit`, `npm test` (весь набор), `npm run build` третьим прогоном** — Validate зелёный на точном материале `a325294d` (headSha сверен), продуктовый код между r2 и r3 не менялся; перегон дал бы тот же результат, что уже подтверждён r2 и Validate. - **Полное измерение перф-профилей (`benchmark:compare`)** — не перегонял: код пути загрузки не менялся между r2 и r3, r2 уже воспроизвёл результат независимо на изолированном `git worktree`, эта дельта его не касается. - **Смоки `demo/smoke_*.mjs`** — не перегонял ни одного: диф r2→r3 не затрагивает `src/**`/`demo/**`, `smoke-select` на этой дельте не даёт ни одного совпадения (проверил: диф ограничен `docs/**`, инструмент по символам кода не находит кандидатов). - **`npm run golden:verify`** — не запускал: диф не меняет рендер ни байтом, визуальная приёмка `--identical` уже доказала попиксельное совпадение всех 11 кадров с закоммиченными PNG. - **`npm run invariants`, `python -m pytest tests_backend`** — geometry и Python-бэкенд этой дельтой не затронуты (не были затронуты и всей веткой, подтверждено r2). ## Гейты: что прогнал сам и почему | Гейт | Прогнан | Результат | |---|---|---| | `node scripts/check-docs.mjs` | да (предмет находки r2, обязателен) | зелёный: «Documentation checks passed (7 files, 12 external links)» | | `git diff 462b5645..a325294d` (полный, не только имена файлов) | да | 2 файла: отпечаток + публикационный документ r2 | | `cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` | да | совпадают побайтово | | `node scripts/process-gate.mjs` | да | пройден, 2 предупреждения (унаследованы, не новые) | | `gh run view 34513504274 --json headSha,conclusion` | да | `headSha` = материал раунда, `conclusion: success` | | `sha256sum` тела issue | да | отличается от обеих предыдущих записей — находка по #517, разобрана в тексте (не блокирует) | | `npx tsc --noEmit`, `npm test`, `npm run build` (свежий прогон) | нет | Validate зелёный на точном SHA, продуктовый код с r2 не менялся | | Perf `benchmark:compare` (оба профиля) | нет | код пути загрузки не менялся с r2, где уже измерено независимо | | `demo/smoke_*.mjs` | нет | дельта не касается `src/**`/`demo/**`, кандидатов ноль | | `npm run golden:verify` | нет | визуальный контент не менялся, доказано `--identical` | | `npm run invariants`, `pytest tests_backend` | нет | geometry/backend вне диапазона всей ветки | ## Вердикт Зелёный. High: 0, Medium: 0. Единственная находка r2 (Medium-1, `check-docs` красный из-за устаревшего отпечатка скриншотов) закрыта минимальной и точной правкой — один файл, только отпечаток, ни одного изменённого PNG; перепроверено прямым прогоном гейта на материале раунда, не на слово автора. Продуктовый код не менялся с r2, чья находка High (регрессия первого кадра) и все AC1–AC6 уже были независимо перепроверены измерением и остаются в силе. Новое, второе расхождение тела issue с зафиксированным хешем ревью ТЗ (#517) названо и разобрано — аддитивно, соответствует коду, возврата в spec не требует. Задача готова к пре-релизной очереди. --- ## Материал раунда - Ветка: `issue/520-first-frame`, коммит `a325294d7a9d8ef4ecfeab2ed3e248318a650ab5`. - Родитель материала r2: `462b56453cd6948c083e62ad8c92ec29aeb022b2` (`docs/reviews/CODE-REVIEW-520-r2.md`). - Тело issue на момент ревью: sha256 `f318b25f5815fa66aeaa7cbc5ce1d63e23fab0a8e50493962634b01e0ca86e96` (получено `gh issue view 520 --json body -q .body | sha256sum`), отличается от записанного в SPEC-REVIEW-520-r3 (`60492a3aec5f…`) и от значения, названного в CODE-REVIEW-520-r2 (`4524f04412...`) — разобрано в тексте, возврата в spec не требует. - Вердикт этого документа: зелёный · High 0 · Medium 0 --- ## Материал раунда - Ветка: `issue/520-first-frame`, коммит `a325294d7a9d` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `f7b30b932911c9f15765594194a6a0b7852d9f1c` ``` git log --all --format='%H %T' | grep f7b30b932911 ``` - Тело issue: `80073dd20133cdbba0bc717c7996f490c2d9010734fecedc11fcf8f894190143` - Вердикт конвейера: `green` · High 0