Files
houseplan-card/docs/reviews/SPEC-REVIEW-39-r3.md
2026-08-29 06:28:19 +00:00

17 KiB
Raw Permalink Blame History

SPEC-REVIEW-39-r3

  • Issue: https://github.com/Matysh/houseplan-card/issues/39 — «[HP-UX-09] большие подложки»
  • Этап: ТЗ на ревью (PROCESS.md §2.4)
  • Заход: r3 · блокирующих циклов израсходовано 2/4 (зелёные вердикты цикл не образуют, #227)
  • ТЗ: docs/specs/039-large-backdrops.md, ревизия 4, SHA 9431a5ce
  • Предыдущий вердикт: жёлтый, r2, SHA f787de99 (docs/reviews/SPEC-REVIEW-39-r2.md) — 0 High, 2 Medium (r2-M1, r2-M2)
  • Трек: полный (владелец, 2026-08-15: «P3, polish/tech-debt, обычный трек») — без изменений

Скоуп проверки (дельта r2→r3)

Дельта локальна и полностью укладывается в границы одной правки одного файла: git diff f787de99..9431a5ce -- docs/specs/039-large-backdrops.md — 22 добавленных / 7 удалённых строк, ничего больше (git diff --stat f787de99..9431a5ce показывает только этот файл плюс публикацию docs/reviews/SPEC-REVIEW-39-r2.md, которую я не оцениваю как предмет ревью — это артефакт прошлого раунда). Единственный коммит в диапазоне — 9431a5ce docs: #39 spec revision 4 per SPEC-REVIEW-39-r2 (Issue: #39, User-Visible: no). Ребейза на ушедший вперёд dev не было (f787de99 — прямой родитель по линии этого файла). Контракт поведения не менялся: ревизия 4 отвечает точечно на r2-M1 и r2-M2, новых AC не добавляла, новой подсистемы не задела. Объём дельты не сопоставим с исходной задачей — веду по дельте согласно PROCESS.md §2.9/§2.10, полный разбор с нуля не требуется.

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

Находка r2 Чем закрыта Где это видно
r2-M1 — AC4б без названного способа доказательства: не назван триггер decode-fail/таймаута в смоке, не проверяется ключ тоста и сброс инпута, ни один мутант не ловит регресс «автофолбэк на оригинал» Раздел «Тесты и мутанты» получил абзац «Hard фаза 2 в смоке»: window.createImageBitmap подменяется на (а) reject — путь decode-fail, (б) вечно висящий Promise — путь таймаута; названы 4 ассерта (тост содержит _t('backdrop.downscale_failed'), <input type=file>.value сброшен, _spaceDialog.planFile === null, повторный выбор открывает диалог заново) плюс счётчик обращений к upload/planFile со старым оригиналом; в реестр мутантов добавлен пункт (5) — подмена catch фазы 2 продолжением старого пути ловится смоком по этому счётчику docs/specs/039-large-backdrops.md:176–186
r2-M2 — «Release-артефакты» не называет security даже отрицательно, хотя вводится ручной парсер заголовков от пользователя Добавлен пункт «security (явное решение)»: парсер читает только фиксированные оффсеты/длины уже полученного ArrayBuffer, ни одно поле файла не становится размером аллокации, w×h — арифметика с защитой от переполнения через Number-границы и отказом unknown на неправдоподобных значениях, любые исключения → unknown; юнит-таблица получает fuzz-набор враждебных заголовков (нулевые/гигантские длины чанков, обрезанный VP8X, SOF без длины); SVG-путь парсером не тронут docs/specs/039-large-backdrops.md:214–221

Обе находки r2 закрыты полностью, а не частично: r2-M1 требовал по своей формулировке «правки» назвать способ форсировать decode-fail/таймаут — «мок window.createImageBitmap… на усмотрение автора, но названный» — ревизия 4 называет ровно этот способ и для decode-fail, и для таймаута, плюс закрывает все перечисленные в r2 пробелы (тост, сброс инпута, мутант на автофолбэк). r2-M2 требовал одну строку с явным решением по security — ревизия 4 даёт больше запрошенного минимума (перечисляет конкретные защитные инварианты парсера и связывает их с юнит-фикстурами), но это не выход за скоуп: те же фикстуры уже были обещаны разделом «Тесты и мутанты» ревизии 3, здесь их только называют явно под заголовком security.

Унаследовано из r2 (и транзитивно из r1)

Без повторной проверки — дельта их не касается:

  • Технические анкеры кода (_pickPlanFile, ручной base64-цикл, aspect через <img>, MAX_FILE_BYTES, квота, транзакция staging-до-save) — сверены построчно в r1 (SHA 156be645), текст этих мест дельта r2→r3 не меняла.
  • Бенчмарк-матрица (4–165 МП, headless Chromium) и константы src/backdrop-probe.ts (WARN_DECODED_BYTES, HARD_DIMENSION, DOWNSCALE_TARGET_PX) — не менялись со ревизии 2/3.
  • AC1–AC3, AC5–AC9, AC4/AC4б по своей формулировке (не по способу доказательства) — не менялись в этой дельте, признаны проверяемыми в r1/r2.
  • Раздел «Сценарий» и «До/После» (закрытие M1 из r1) и полный список i18n-ключей en/ru (закрытие M2 из r1) — не менялись, проверены в r2 (SPEC-REVIEW-39-r2.md, SHA f787de99).
  • Тост как технически достижимый механизм из _pickPlanFile → this.host._showToast — сверено в r2 по коду houseplan-editor-runtime.ts:8252 и houseplan-card.ts:2495; дельта r3 не меняла ни вызывающий код, ни описание тоста, только добавила новый ключ в уже существующий канал.
  • docs/SCOPE.md J4 и standing rules (never-delete-a-file, lock invariant) — нерелевантны/не задеты, сверено в r1 и r2, дельта r3 текст сценария и скоупа не трогала.
  • Терминология («подложка», диалоги hp-dialog) соответствует docs/USER-GUIDE.ru.md — сверено в r1, новых терминов в дельте r3 нет.
  • Low-находки r2 («чего не проверял»): возможное несоответствие backdrop.unknown_title/large_title и наличие ли у hp-dialog готового сценария с тремя кнопками действия — оставлены открытыми решением ревьюера r2 (не блокируют DoR), дельта r3 их не касалась, повторно не поднимаю.

Как проверялось в этом раунде

  1. Нашёл вердикт r2 и его SHA в опубликованном документе docs/reviews/SPEC-REVIEW-39-r2.md:6 (f787de99, назван явно) и в комментарии issue от 2026-08-29T06:22:04Z — совпадают.
  2. git diff f787de99..9431a5ce -- docs/specs/039-large-backdrops.md построчно (полный вывод см. выше в «Скоуп проверки»).
  3. git diff --stat f787de99..9431a5ce — подтвердил, что дельта не тронула ничего вне docs/specs/039-large-backdrops.md (второй файл в диапазоне — публикация прошлого документа ревью, не предмет этого раунда).
  4. Прочитал итоговый файл целиком (232 строки), не только diff-хунки — сверил новый абзац «Hard фаза 2 в смоке» и мутант (5) против UX-контракта фазы 2 (:99–104) и AC4б (:157–159): триггер, тост, сброс инпута и «нет автофолбэка» совпадают дословно с тем, что описано в UX и AC.
  5. Сверил новый security-абзац (:214–221) с разделом «Диагностика: заголовки, не decode» (:75–85) и «Риски» (:194–197, «Зоопарк заголовков») — противоречий нет, детали (фиксированные оффсеты, отказ в unknown) согласуются, а не дублируют другим числом.
  6. gh issue view 39 --json comments — прочитал все 6 комментариев целиком, включая комментарий владельца о ревизии 4 (совпадает с diff, новых устных решений или продуктовых вопросов не содержит).
  7. docs/specs/README.md (список обязательных release-артефактов) — сверил, что после правки перечень «changelog / docs / i18n / golden / performance / security» закрыт по всем пунктам буквально, а не молчанием.
  8. Проверил метки issue (gh issue view 39 --json labels): S4-spec-review, P3, polish, tech-debt — трек по-прежнему полный, файл ТЗ обязателен (не small), что и наблюдается.

Гейты

Diff — только docs/specs/039-large-backdrops.md (плюс публикация прошлого review-документа), 0 файлов в src/**/custom_components/**/test/**/ demo/**. Как в r1 и r2:

Гейт Применимо? Причина
npx tsc --noEmit / npm test / npm run build нет диапазон не содержит кода
node scripts/check-docs.mjs нет src/** не тронут
npm run invariants нет геометрия/layout/толщина стен не затронуты
browser-смоки, golden:verify, pytest tests_backend нет реализации по-прежнему нет; смоки/юниты из ТЗ (test/backdrop-probe.test.mjs, demo/smoke_backdrop_guard.mjs) не существуют

Намеренно ничего не прогонял: предмет этапа spec — текст ТЗ, а не код, и прогон гейтов над docs-only диапазоном не проверил бы ничего по существу.

Находки

Нет. High: 0. Medium: 0. Обе находки r2 закрыты полностью (см. таблицу выше), новых Medium/High дельта не вносит.

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

  • r2-M1 закрыта: способ форсирования decode-fail (reject) и таймаута (вечно висящий Promise) назван явно через мок window.createImageBitmap; все 4 ранее не названных ассерта (ключ тоста, сброс инпута, чистый staging, повторный выбор) теперь в тексте; мутант (5) ловит именно ту регрессию, ради которой AC4б вводился («молча грузить оригинал, от которого отказались, — нечестно»).
  • r2-M2 закрыта: security-решение явное, содержательное (fixed-offset parsing, без file-controlled allocation size, fail-closed в unknown, fuzz-таблица) и не противоречит уже описанной диагностике и рискам.
  • Новый текст согласован с остальным документом: UX-контракт фазы 2 (:99–104), AC4б (:157–159) и обновлённые «Тесты и мутанты» (:176–186) описывают одно и то же поведение одними и теми же терминами (тост, сброс инпута, staging), расхождений в формулировках нет.
  • «Откат», «Вне скоупа», i18n-список и «Риски» дельтой не менялись и остаются согласованными с новым текстом.
  • Владельцу вопросов не задавалось ни в этом, ни в предыдущих раундах — обе находки r2 были техническими и решены автором ТЗ в своей компетенции.
  • DoR §2.5 по совокупности трёх ревизий (r1→r4) закрыт буквально: сценарий и «до/после» есть, AC1–AC9(+4б) пронумерованы с указанием способа доказательства, i18n-ключи en/ru перечислены явно, риски и release-артефакты (включая security) названы, откат — один revert без миграции конфига.

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

  • Реальное поведение в браузере — реализации по-прежнему нет, test/backdrop-probe.test.mjs и demo/smoke_backdrop_guard.mjs не существуют; их «умение падать» — предмет код-ревью (AC4б, мутант 5 в частности).
  • Точна ли оценка «10 с таймаут» будет действительно проверяться смоком без реального 10-секундного ожидания в CI: текст называет механизм (мок глобала на вечно висящий Promise), но не говорит, укорачивается ли сам таймер реализации под тестовым флагом. Это не было отдельно сформулировано как обязательное требование в «Правке» r2-M1 (там был предложен выбор «мок глобала или ускоренный таймер — на усмотрение автора, но названный»), и названный вариант формально закрывает находку. Оставляю Low, не блокирует: это решение исполнителя о скорости прогона смока, не о корректности AC4б, и он вправе сам выбрать — ускорить таймер под тестовым флагом или мириться с 10-секундным ожиданием в смоке.
  • Low-находки, унаследованные из r2 (backdrop.unknown_title vs переиспользование large_title; поддержка hp-dialog трёх кнопок действия) — не переоценивал, дельта их не касалась, решение реализации остаётся открытым по тем же причинам, что в r2.
  • Точность чисел бенчмарка (4–165 МП) — не пересчитывал, как и в r1/r2.

Вердикт

0 High, 0 Medium. Обе Medium-находки r2 (r2-M1, r2-M2) закрыты полностью и без остатка, новых блокирующих находок дельта r2→r3 не вносит. ТЗ (ревизия 4) удовлетворяет §7.1 и DoR §2.5 по совокупности всех четырёх ревизий.

Вердикт: зелёный · заход r3 · блокирующих циклов 2/4 · High: 0 · Medium: 0