Files
claude[bot] e19f5b0a0e
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Canceled after 0s
Проверка (CI) / Классификация изменённых файлов (push) Canceled after 0s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Canceled after 0s
Проверка (CI) / HACS: валидация репозитория (push) Canceled after 0s
Проверка (CI) / Hassfest: манифест интеграции (push) Canceled after 0s
Проверка (CI) / Мутанты по диффу (1/6): затронутые свидетели краснеют (push) Canceled after 0s
Проверка (CI) / Мутанты по диффу (2/6): затронутые свидетели краснеют (push) Canceled after 0s
Проверка (CI) / Мутанты по диффу (3/6): затронутые свидетели краснеют (push) Canceled after 0s
Проверка (CI) / Мутанты по диффу (4/6): затронутые свидетели краснеют (push) Canceled after 0s
Проверка (CI) / Мутанты по диффу (5/6): затронутые свидетели краснеют (push) Canceled after 0s
Проверка (CI) / Мутанты по диффу (6/6): затронутые свидетели краснеют (push) Canceled after 0s
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Canceled after 0s
Проверка (CI) / Бандл головы dev для стенда (push) Canceled after 0s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Canceled after 0s
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Canceled after 0s
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Canceled after 0s
Проверка (CI) / Смоки: все шарды зелёные (push) Canceled after 0s
Проверка (CI) / Golden-кадры против принятых эталонов (push) Canceled after 0s
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Canceled after 0s
Проверка (CI) / Геометрия: TS/Python parity исполнена (push) Canceled after 0s
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Canceled after 0s
Проверка (CI) / Доказательство выполненных проверок (push) Canceled after 0s
docs: review document for #713
Issue: #713
User-Visible: no
2026-09-30 11:31:11 +00:00

18 KiB
Raw Permalink Blame History

SPEC-REVIEW-713-r2

Issue: #713 · «Плоские предметы декора и разный сдвиг устройств при переключении в 2.5D» Этап: spec (PROCESS.md §2.4) · Трек: ask · Заход: r2 · блокирующих циклов израсходовано 1/4 до этого раунда

Материал раунда

  • Ветка dev, коммит 90d38b168508cf2069b11e1f325ae68d9848c500 (дерево a372ab54a48802298add970ceb07056b09c25a72), рабочая копия уже на нём. Код продукта в этом раунде не менялся — этап spec, S4-spec-review.
  • Тело issue #713, раздел ## ТЗ, текущая редакция (после правки по r1).
  • Комментарий автора 2026-09-30T11:24:41Z: перечень правок по SPEC-REVIEW-713-r1.md — «M1: К6 переписан… AC5 расширен на (а), новый AC11 на (в) и холодный старт… Дополнительно: К8 — кадр больше не резервирует 48 px под сдвиги #651, AC9 дополнен».
  • Документ предыдущего раунда: docs/reviews/SPEC-REVIEW-713-r1.md (вердикт жёлтый, Medium 1 — M1, High 0).
  • Код прочитан только там, где дельта делает утверждение ТЗ проверяемым: src/houseplan-card.ts:3292-3340 (_warmAdoptViewport), src/iso-scene-render.ts:683-732 (resolveIsoOverlayFitEnvelope), src/iso-overlays.ts:13 (ISO_OVERLAY_MAX_NUDGE_CSS_PX), demo/smoke_warm_remount.mjs (существующий паттерн ремаунта тёплой карточки, на который ссылается новый план автотестов). Никакой продуктовый код в рамках этого ревью не менялся.

Дельта против r1

git diff неприменим к телу issue; дельта — точечная правка текста ТЗ, подтверждённая построчно: сравнение цитат из SPEC-REVIEW-713-r1.md (старые формулировки К6/AC5/AC9, процитированные в находке M1) с текущим текстом issue. Изменения ограничены:

  • К6 — добавлен явный список «где действует» с под-пунктами (а)/(б)/(в) и отдельно расписан путь (в) — восстановление тёплого памятника из другой проекции — с формулой пересчёта zoom и поведением при незагруженном 2.5D-рантайме;
  • АС5 — расширен явной формулировкой для (а): «сохранение настройки при zoom ≠ 1 и сдвинутой камере оставляет viewBox прежним… а zoom равен fit'.w / view.w новой проекции — в обе стороны»;
  • новый АС11 — покрывает (в) и холодный старт с включённым 2.5D без памятника;
  • К8 и АС9 — добавлено утверждение «кадр больше не резервирует 48 CSS px под сдвиги #651»;
  • план автотестов — строка про demo/smoke_iso_flat_parity.mjs расширена: «AC2–AC5 и AC11 (ремаунт через warmBoot с конфигурацией, сменённой на сервере), со свидетелем на старом коде».

Остальные разделы (Сценарий, До/После, Проблема, Скоуп/Не-скоуп, К1–К5, К7, К9, UX, модель данных, i18n, риски, откат, release-артефакты, «Принятые предположения») текстуально не изменились — сверено построчно с цитатами и пересказом r1-документа, расхождений не найдено. Разбор по ним — в разделе «Унаследовано из r1» ниже, повторно не проверяю.

Дельта локальна (правка одного контрактного пункта и двух AC по итогам одной находки прошлого раунда) — полный разбор задачи не требуется, проверяю только то, что дельта задевает: закрытие M1 и отсутствие новых противоречий в изменённом тексте.

Как проверялось

Закрытие M1

M1 (r1): К6 называл три пути переноса камеры между проекциями, а доказательство (AC5) покрывало только два — (б) полностью, (а) косвенно через AC2; путь (в), восстановление тёплого памятника из другой проекции (_warmAdoptViewport, реальный код src/houseplan-card.ts:3292-3340), не имел ни AC, ни строки в плане автотестов.

Проверено, что правка закрывает находку по существу, а не только по форме:

  1. Текст контракта. К6 теперь явно перечисляет (а)/(б)/(в) с формулой z' = fit'.w / view.w и уточняет поведение (в) при незагруженном 2.5D-рантайме («пересчёт выполняется при его загрузке, без сдвига картинки») — то есть явно относится и к отложенной загрузке, не только к немедленному случаю.
  2. AC. Новый AC11 текстуально соответствует К6(в): «тёплый памятник, сохранённый в плоском виде при zoom ≠ 1 и сдвинутой камере, после включения 2.5D на сервере и ремаунта карточки восстанавливается с тем же viewBox… и zoom fit'.w / view.w… в том числе когда рантайм 2.5D догружается после ремаунта» — это дословно путь (в) плюс явный ленивый подслучай. Отдельным предложением в AC11 закрыт и «холодный старт без памятника» (тоже часть К6), который в r1 вообще не был поименован отдельно.
  3. Способ доказательства назван, а не декларирован. План автотестов указывает конкретный файл demo/smoke_iso_flat_parity.mjs и конкретный механизм — «ремаунт через warmBoot с конфигурацией, сменённой на сервере». Проверено, что это не выдуманный ярлык: паттерн «снять со страницы старый <houseplan-card>, создать новый с тем же localStorage, дождаться warm-адопта» уже существует и рабочий — demo/smoke_warm_remount.mjs (снятие/пересоздание карточки, проверка _warmSlot, _zoom после ремаунта). Новый смок может переиспользовать этот же паттерн, добавив только смену settings.volumetric_view между c1.remove() и созданием c2 — реалистичный, не гипотетический план.
  4. Код-путь (в) действительно ведёт к найденному дефекту сегодня — значит AC11 не вакуумен, будущий тест «умеет упасть». Прочитан src/houseplan-card.ts:3320-3329:
    this._zoom = vp.zoom;
    ...
    if (!sameProjection && vp.logicalCenter) {
      const center = projection === 'iso' ? projectPlanPoint(...) : [...];
      this._applyView(vp.zoom, center[0], center[1]);
    }
    
    При смене проекции (!sameProjection) код пересчитывает только центр, но переиспользует старый vp.zoom без формулы fit'.w / view.w — ровно то, что К6(в)/AC11 требуют исправить. На сегодняшнем коде AC11 действительно красный, а не тавтология.
  5. AC9 и К8 («кадр больше не резервирует 48 px под сдвиги #651») — проверено чтением src/iso-scene-render.ts:696-732 (resolveIsoOverlayFitEnvelope) и src/iso-overlays.ts:13 (export const ISO_OVERLAY_MAX_NUDGE_CSS_PX = 48;): pad = ISO_OVERLAY_MAX_NUDGE_CSS_PX * scale на строке 710 — это и есть резерв 48 CSS px под кластерный сдвиг #651, который ТЗ называет. Утверждение К8 технически точное, а не догадка: код, который перестаёт быть нужным при отключении поиска #651 в живом пути, назван верно и по имени, и по значению константы.

Вывод: M1 закрыта по существу — недостающий путь (в) получил свой AC с конкретной формулой, конкретный тест назван и его механизм реалистичен, затронутый код-путь подтверждён чтением и сегодня действительно ведёт себя не так, как того требует новый контракт (тест не может оказаться тавтологией).

Находки

Новых Medium/High в дельте не найдено.

L1 (Low, снимается с записью). AC2 и AC3 явно помечены «свидетель красный на старом коде», AC11 — нет, хотя по смыслу и по факту (см. п.4 выше) он тоже красный на текущем коде. Формально план автотестов покрывает это одной фразой для группы AC2–AC5 и AC11 разом («со свидетелем на старом коде»), так что пробела в доказательстве нет — только асимметрия форматирования внутри самого AC11. Снимаю без возврата автору: проверено чтением кода (п.4 выше), что тест действительно будет падать на нынешнем _warmAdoptViewport, и план автотестов эту гарантию уже даёт на уровне файла.

Что проверено и корректно

  • M1 закрыта: путь (в) К6 получил AC11 с формулой и явным ленивым подслучаем, план автотестов называет конкретный файл и переиспользуемый паттерн ремаунта.
  • Новое утверждение К8/AC9 («кадр не резервирует 48 px») подтверждено чтением resolveIsoOverlayFitEnvelope и константы ISO_OVERLAY_MAX_NUDGE_CSS_PX = 48 — не догадка.
  • Код-путь _warmAdoptViewport (строки 3292–3340, в частности ветка !sameProjection на 3325) подтверждает, что AC11 не тавтологичен: сегодня zoom при смене проекции не пересчитывается по формуле К6.
  • Остальной текст ТЗ (не задетый дельтой) не изменился со времени r1 — сверено построчно с цитатами r1-документа.
  • Открытых вопросов владельцу не осталось; технических споров, вынесенных на владельца, в дельте нет.

Чего не проверял

  • Реализацию — её нет (этап S4-spec-review, правило №1 AGENTS.md).
  • npx tsc --noEmit / npm test / npm run build — не прогонял: кода нет, node_modules не установлен (зависимости и Chromium на этапе spec не ставятся, #696), гейты неприменимы к этому этапу.
  • node scripts/smoke-select.mjs, golden:verify, pytest tests_backend, npm run invariants — неприменимы: нет диффа продуктового кода, который можно было бы прогнать через эти гейты.
  • Мутанты scripts/mutation-registry.mjs — задача сама откладывает перепривязку якорей без прогона (owner rule #709); это решение ТЗ, а не пробел ревью.
  • Разделы, не задетые дельтой (К1–К5, К7, К9, Скоуп/Не-скоуп, риски, откат, release-артефакты, производительность/бюджеты) — не проверял заново; унаследованы из r1 (см. ниже).

Закрытие раунда r1

Находка Чем закрыта Где это видно
M1 — К6(в) («тёплый памятник из другой проекции») не имел AC и строки в плане автотестов К6 переписан с явным списком (а)/(б)/(в) и формулой z' = fit'.w / view.w; добавлен AC11, покрывающий (в) и холодный старт; план автотестов явно называет AC11 в строке про demo/smoke_iso_flat_parity.mjs Тело issue #713, разделы «Контракт поведения → К6» и «Критерии приёмки → AC11», «План автотестов»; проверено чтением src/houseplan-card.ts:3320-3329, что путь действительно ведёт себя иначе, чем требует новый К6

Унаследовано из r1

Без повторной проверки в этом раунде принято (документ и материал того раунда — docs/reviews/SPEC-REVIEW-713-r1.md, материал: dev@683c9250b95a, дерево 4e114c6e9445be19c037c6d01d995e8175bfe45d):

  • Обязательные разделы §7.1 присутствуют полностью.
  • К1 (isoPlaneMatrix, тождество пола, H·sin 20°) — сверено чтением src/iso-projection.ts.
  • К7 (ключ глубины проёма s·y + z) и независимость очереди стен от этой формулы — сверено чтением src/iso-openings.ts:376-379, src/iso-walls.ts:107,147,154,206.
  • К4 vs подъём плитки #649 (0.075·D не зависит от marker.size) — сверено чтением src/iso-tiles.ts, styles/iso-tiles.styles.ts.
  • К6(а)/(б) и код _convertProjectionView/_syncVolumetricSetting/ _warmAdoptViewport как реальные, а не гипотетические код-пути — сверено чтением src/houseplan-card.ts:2331-2358, 3292-3340.
  • gs.volumetric_view_hint в en.json/ru.json действительно уже обещает «Decor and the flat plan stay unchanged» — сверено чтением i18n-файлов.
  • Owner-решение «вариант Б» подкреплено реальным замером на buildIsoOverlayRenderScene, не голословно.
  • Бюджет стартового графа (AC10, #438) назван явно, запас 172 Б.
  • i18n и модель данных корректно объявлены «без изменений».

Вердикт

Зелёный. M1 закрыта по существу: путь К6(в) получил формулу, AC11 и названный тест с реалистичным механизмом (переиспользуемый паттерн demo/smoke_warm_remount.mjs), а чтением кода подтверждено, что этот AC не тавтологичен — _warmAdoptViewport сегодня действительно не пересчитывает zoom по новой формуле при смене проекции. Новое утверждение К8/AC9 про резерв 48 px проверено и точно совпадает с кодом. High — 0, Medium — 0 (в задаче нет открытых Medium); один Low снят с записью, не требует возврата автору. ТЗ готово к S5-ready.


Материал раунда

  • Ветка: dev, коммит 90d38b168508 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: a372ab54a48802298add970ceb07056b09c25a72
    git log --all --format='%H %T' | grep a372ab54a488
    
  • Тело issue: 2305e229564d42ff52bc6d6672c579214fcd49ec80df28e2f111ea726179e7ab
  • Вердикт конвейера: green · High 0