# SPEC-REVIEW-474-r2 - **Issue:** #474 — «Стартовый граф: арт мебели уходит в ленивый чанк» - **ТЗ:** `docs/specs/474-lazy-furniture-art.md` - **Предыдущий раунд:** r1, вердикт красный, документ `docs/reviews/SPEC-REVIEW-474-r1.md` (коммит `54efafbd`), материал на SHA `bb180899ff7932dd81f5a3afc8fe4975ede8df76`. - **Правка автора:** коммит `ce08dc2c7102d3923e438dbdb10a788ab899e37a` («докс: ТЗ #474 r1 — редактор передаёт арт рантайму синхронно, golden-ожидание по образцу iso»). - **SHA материала этого раунда:** `ce08dc2c7102d3923e438dbdb10a788ab899e37a` (единственный файл в дельте — `docs/specs/474-lazy-furniture-art.md`, 27 добавлено / 9 удалено; `git diff bb180899..ce08dc2c -- docs/specs/474-lazy-furniture-art.md`). - **Трек:** полный. Заход r2, блокирующих циклов израсходовано 1 из 4 до этого вердикта. - **Вердикт: жёлтый** (Medium ниже, в скоупе задачи, без High). ## Скоуп разбора Раунд не первый и дельта строго локальна: один файл, один коммит поверх материала r1, оба абзаца дельты — прямой ответ на High- и Low-находки r1 (третья точка готовности арта в редакторе; неточная аналогия в риске golden). Полного разбора ТЗ заново не делаю — см. «Унаследовано из r1» ниже. Разбираю: (а) закрывает ли правка обе находки r1 по существу, а не по заявлению; (б) не завела ли сама правка новую неоднозначность в контракте или AC, которую дельта затрагивает (изменённые разделы: §2 п.3, §4.2 таблица методов, §4.3 «Запуск и бут», §4.6 таблица свидетелей, §6 AC7, §6.2 риск golden, §8 затронутые файлы). ## Как проверялось - `git diff bb180899..ce08dc2c -- docs/specs/474-lazy-furniture-art.md` — построчно. - Каждое новое фактическое утверждение сверено с деревом на `ce08dc2c` (код не менялся с r1, дерево то же, что и в r1, плюс сам ТЗ): - `src/editor-runtime-loader.ts` — подтверждён `EditorRuntimeLoaderOptions.install: (runtime) => void`, вызывается из `_loadWithRetry()` один раз после конструктора и до `_setState('ready')` — ровно то, на что ссылается новый текст §4.3 («install в EditorRuntimeLoader редактора»), это не домысел; - `src/decor-image-editor.ts:271/282` (`renderFurniturePalette`, `preview`) и `src/houseplan-editor-runtime.ts:4931` (`_renderFurniturePlacementPreview`) — имена и точки вызова, названные в правке, существуют, номера строк сходятся с точностью до контекста; - `demo/golden/harness.mjs:818` (`ensureIsoRuntime`) — реальный прецедент, на который теперь ссылается исправленный риск в §6.2, вместо снятой ложной аналогии с языком; - полный текст ТЗ на `ce08dc2c` прочитан целиком, чтобы дельта (два абзаца) не проверялась в отрыве от остального контракта — так нашлась находка ниже. - Гейты (`typecheck`/`test`/`build`/`bundle:budget`) не гонялись: этап spec, продуктового кода по задаче нет — прогон был бы гейтом на пустом месте, как и в r1. ## Закрытие раунда r1 | Находка r1 | Чем закрыта | Где это видно | |---|---|---| | **High** — единственная точка запуска `ensure()` (приём конфига) не покрывает открытие палитры/призрака в редакторе на конфиге без мебели; AC7 ломается для сценария «первый предмет» | Добавлена третья, синхронная точка готовности: `FurnitureArtRuntime.adopt(art, fingerprint)`, вызываемая из `install` редакторного `EditorRuntimeLoader` при статическом импорте арта редактором. `pending` в редакторе теперь структурно невозможен: `adopt` отрабатывает до первого рендера палитры, не дожидаясь `ensure()`/содержимого конфига | §4.2 (новая строка `adopt` в таблице методов), §4.3 абзац «Редактор — третья точка, синхронная (ревью r1)», AC7 переформулирован под «pending в редакторе не бывает», новый свидетель `furniture-art-editor-adopt-skipped` (§4.6), гард которого — `smoke_furniture` на конфиге без мебели (тот же смок и то же условие `decor=[]`, что и в воспроизведении r1) | | **Low** — риск §6.2 ссылался на несуществующее ожидание `LANGUAGE_RUNTIME` в harness «как ждёт язык» | Формулировка заменена на реальный прецедент `ensureIsoRuntime`; дополнительно риск усилен требованием, чтобы harness бросал, если в сцене с мебелью `.dfurn` меньше числа предметов конфига (fallback должен быть красным, а не пустым кадром) | §6.2, строка «Golden-сцены с мебелью станут недетерминированными» | Обе находки закрыты по существу, не только по заявлению: механизм `adopt` проверяется реальным контрактом `EditorRuntimeLoader.install`, а не новым самостоятельным изобретением, и ссылка на `ensureIsoRuntime` — существующий код, а не повторная неточная аналогия. ## Унаследовано из r1 Без повторной проверки в этом раунде принято всё, чего дельта не касалась (документ `docs/reviews/SPEC-REVIEW-474-r1.md`, SHA `bb180899`): - сценарий и «что человек увидит» (§1.1–1.2), продуктовая рамка и связь с персоной `docs/SCOPE.md`; - разрез каталог/арт (§4.1) и единственная точка чтения арта во View (`houseplan-card.ts` `furnitureGraphic`), перевод магнита `furniture-placement` на `furnitureSymbol`; - корректность образца `LanguageRuntime` и нонс-механизма (`hp_retry`) как базы контракта `FurnitureArtRuntime`; - `core-file-budget` и заявление «ядро не растёт» (числа не менялись дельтой); - не-скоуп по `houseplan-space-card` (§3); - i18n-ключ `toast.furniture_art_load_failed` (не занят, по образцу `toast.locale_load_failed`); - нумерация и полнота обязательных разделов §7.1, явный блок предположений (§9), связь issue↔ТЗ. Эти пункты дельта r1→r2 не трогает ни текстуально, ни по содержанию (см. diff выше — правки только в §2 п.3, §4.2, §4.3, §4.6, AC7, §6.2, §8), поэтому переносятся без повторной проверки. ## Находки этого раунда ### [Medium, в скоупе] Счётчик свидетелей в AC9 и §8 не обновлён после добавления седьмого **Файл:** `docs/specs/474-lazy-furniture-art.md`, AC9 (раздел 6) и раздел 8 («Затронутые файлы»). Правка r1→r2 добавила в таблицу §4.6 новую строку — седьмой свидетель `furniture-art-editor-adopt-skipped` (гард `install` редактора не вызывает `adopt`) — не убрав ни одной из прежних шести. Итого в §4.6 сейчас семь строк: `furniture-art-eager-import`, `furniture-art-fallback-never-settles`, `furniture-art-no-retry-nonce`, `furniture-art-boot-gate-ignored`, `furniture-placement-needs-art`, `furniture-art-fingerprint-unchecked`, `furniture-art-editor-adopt-skipped`. При этом: - AC9: «**Шесть** свидетелей §4.6 в реестре, каждый «поймано 1 из 1»» — число не правлено этим коммитом (в r1-версии ТЗ, где строк было шесть, формулировка была верна: r1-документ ревью прямо фиксирует «шесть свидетелей §4.6», см. `docs/reviews/SPEC-REVIEW-474-r1.md`, «Чего не проверял»); - §8 «Затронутые файлы»: «`scripts/mutation-gate.mjs` — **шесть** мутантов» — та же стале число, тот же коммит её не тронул. Это не спор о поведении — контракт по существу понятен и AC7 закрыт корректно (находка выше). Но code review будет сверять реестр мутантов с AC9 буквально («шесть, каждый поймано 1 из 1») и рискует не заметить необходимость завести седьмой мутант `furniture-art-editor-adopt-skipped` в `mutation-gate.mjs`, если проверять только по счётчику, а не по факту чтения §4.6. AC должен быть самодостаточным числом, а не требовать сверки с другой таблицей, которая была изменена позже. **Что нужно ТЗ:** заменить «шесть» на «семь» в AC9 и в §8 (`scripts/mutation-gate.mjs` — семь мутантов), либо явно сослаться на §4.6 без числа. Это правка на месте (в скоупе задачи, не отдельный issue) — Medium без High даёт жёлтый вердикт. ## Что проверено и корректно (сверх наследуемого) - Новый метод `adopt` в §4.2 не противоречит остальной таблице: идемпотентность («повторный adopt — no-op»), проверка отпечатка ровно как у `ensure()` — общее защитное свойство теперь явно распространено на оба входа, и `furniture-art-fingerprint-unchecked` в §4.6 обновлён («ensure и adopt»), а не оставлен только для одного пути. - AC7 переформулирован согласованно с текстом §4.3 (то же «pending в редакторе не бывает», тот же список смоков) — не осталось двух разных описаний одного и того же сценария в разных разделах. - §6.2 golden-риск теперь описывает поведение, согласованное с §4.3 (единый бут-гейт `hpboot`, ожидание встроено, а не отдельная golden-специфичная реализация) — не вводит второй, конкурирующий механизм ожидания. - Идентификаторы, на которые ссылается правка (`EditorRuntimeLoaderOptions.install`, `renderFurniturePalette`, `preview`, `_renderFurniturePlacementPreview`, `ensureIsoRuntime`), существуют в дереве под теми же именами — утверждение о поведении не выдано за факт без основания. ## Чего не проверял - Всё, что перечислено в «Унаследовано из r1» — не проверялось повторно, дельта этих разделов не касается. - Численные пороги AC1 и реализуемость семи (после исправления счётчика) свидетелей как мутантов в `scripts/mutation-gate.mjs` — как и в r1, это код, которого пока нет; переносится на code review. - Гейты (`typecheck`/`test`/`build`/`bundle:budget`/`golden`/смоки) не гонялись — этап spec, кода по задаче ещё нет. --- ## Материал раунда - Ветка: `issue/474-lazy-furniture-art`, коммит `ce08dc2c7102` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `b087deec17ea42c9cd87f4738a90b69f7deba132` ``` git log --all --format='%H %T' | grep b087deec17ea ``` - ТЗ `docs/specs/474-lazy-furniture-art.md`, блоб `9ac07c2aad7a940779d0fb060e74524fc492afbb` ``` git log --all --find-object=9ac07c2aad7a940779d0fb060e74524fc492afbb -- docs/specs/474-lazy-furniture-art.md ```