Files
houseplan-card/docs/reviews/SPEC-REVIEW-474-r2.md
2026-09-06 16:10:01 +03:00

14 KiB

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