19 KiB
SPEC-REVIEW-473-r1
- Issue: https://github.com/Matysh/houseplan-card/issues/473
- Этап: spec (ревью ТЗ, PROCESS.md §2.4)
- Заход: r1 · блокирующих циклов израсходовано 0 из 4
- ТЗ:
docs/specs/473-iso-perf-witnesses-and-smoke.md, SHA2cedcc6221e40ea6ead2f48ff7fb37ddc96a51ca - Вердикт: жёлтый
Скоуп ревью
Материал раунда — весь диапазон HEAD..HEAD (первый заход, дельты по
предыдущему раунду нет): два файла, docs/specs/473-iso-perf-witnesses-and-smoke.md
(новый, 125 строк) и одна строка в docs/specs/README.md. Продуктового и
тестового кода нет — задача ещё в статусе ТЗ.
Задача — процессная/инфраструктурная (постфактум-ревью perf-дельты #160 плюс
диффозависимый перф-смок в Validate), класс файлов будущей реализации — только
B (test/**, scripts/mutation-gate.mjs, .github/workflows/validate.yml,
demo/performance/*.json, PROCESS.md). docs/SCOPE.md прямо не применяется:
задача не меняет ни один Core user job, а укрепляет процесс, которым продукт
разрабатывается — это соответствует прецеденту #404/#399/#422 (те же
инфраструктурные ТЗ полного трека).
Как проверялось
Гейты (typecheck/test/build) на этом раунде неприменимы: дифф — два
документных файла, продуктового и тестового кода ещё нет. Прогонять их
незачем, и ссылка в задании на зелёный Validate (2cedcc62) сюда не
адресуется — там просто нечего было ломать.
Разбор шёл построчной сверкой каждого технического утверждения ТЗ с текущим
деревом (2cedcc62, тот же SHA что HEAD):
- построчные ссылки §4 (
iso-scene-render.ts:783,786,805,821,iso-overlays.ts:342-345) — прочитаны и сверены с исходником; - существование и сигнатуры скриптов, упомянутых в §5 (
demo/benchmark_large_house.mjs,demo/benchmark_glow.mjs,demo/performance/compare.mjs,evaluate.mjs) — прочитаны целиком в интересующих местах (valueArg,--variants,--absolute-only,minimumSamples); - существующие бюджеты (
budgets-glow-smoke.json,budgets-large-house-isometric.jsonи др.) — сверены на предмет прецедента «smoke-бюджет как урезанное подмножество полного» и совпаденияhardMaxMsс цифрами регрессии из Проблемы; .github/workflows/validate.yml— jobchanges,reuse,performance_smokeпрочитаны целиком, чтобы понять, как диффозависимый триггер (§5) сочетается с механизмом переиспользования по содержимому (scripts/gate-reuse.mjs) и есть ли ручной способ форсировать путь (workflow_dispatch— проверено по всем workflow, вvalidate.ymlего нет);test/validate-workflow.test.mjs,test/performance-budget.test.mjs— существуют, стиль контрактных тестов (regex по тексту YAML) подтверждает реализуемость AC3/AC4/AC5 как статических проверок;de215578— коммит существует в истории, воспроизведение AC6 технически доступно;docs/specs/README.md— формат строки таблицы сверен с соседними записями.
Находки
Все три — Medium, в скоупе задачи: без High это жёлтый вердикт, правки вносятся в тот же файл ТЗ и проходят повторный цикл (PROCESS.md §2.4, #202). Отдельный issue не заводится.
Medium-1 — AC7 недоказуем предписанным способом
Файл: docs/specs/473-iso-perf-witnesses-and-smoke.md:103
AC7 | На текущем
devоба новых профиля укладываются в потолки за 3 образца | прогон в CI этой ветки
§5 (той же строкой 68-71) делает триггер профилей диффозависимым от
ПРОДУКТОВЫХ путей: perf_iso — если дифф задел src/iso-*, perf_interaction
— если дифф задел src/live-*, src/render-*,
src/houseplan-render-lifecycle.ts, src/houseplan-card.ts. Это выходы job
changes, которая судит дифф ветки против последнего доказанно зелёного
предка (validate.yml:270-284), а не содержимое дерева.
Сама реализация этой задачи, согласно §6, «продуктовый код не меняет» —
дифф ветки состоит из test/**, scripts/mutation-gate.mjs,
.github/workflows/validate.yml, demo/performance/*.json. Ни один из этих
путей не матчит шаблоны src/iso-* / src/live-* / src/render-* /
houseplan-card.ts. Значит на пуше этой самой ветки perf_iso и
perf_interaction резолвятся в false, новые шаги смока не выполняются
вовсе, и «прогон в CI этой ветки» не производит того свидетельства, которое
AC7 требует.
Проверено, что обхода через ручной триггер нет: validate.yml не имеет
workflow_dispatch (в отличие от performance.yml, mutation-gate.yml,
docs-screenshots.yml, у которых он есть) — форсировать классификацию
нечем.
Способ существует (ровно как для AC6 — разовый замер npm run benchmark:large-house-isometric -- --samples=3 --warmups=1 +
benchmark:compare --absolute-only --budgets=..., команда и результат
записываются в issue), но ТЗ называет другой, автоматический и, как показано
выше, недостижимый на этой ветке. Либо AC7 меняет способ доказательства на
ручной (аналогично AC6), либо явно описывает временный форс-коммит в
продуктовый файл для проверки с последующим откатом перед мержем — сейчас в
тексте нет ни того, ни другого.
Medium-2 — --variants=60 не поддерживается скриптом, на который распространён контракт
Файл: docs/specs/473-iso-perf-witnesses-and-smoke.md:74-79
Профили идут с
--variants=60 --samples=3 --warmups=1 --absolute-onlyи своими smoke-бюджетами…
Флаг --variants относится к сценариям, где он читается: demo/benchmark_glow.mjs:27
(requestedVariants = valueArg('variants')?.split(',')...) — там это число
одновременных обновлений состояния ([1, 10, 30, 60]). Оба новых профиля,
large-house-isometric-v1 и large-house-interaction-v1, идут через
demo/benchmark_large_house.mjs (package.json:22,25), который вообще не
читает --variants — там только samples, warmups, target-root,
profile, output (demo/benchmark_large_house.mjs:17-22). Формулировка §5
одной строкой распространяет флаг glow-скрипта на все три профиля — фактическая
ошибка в записанном как готовый контракт наборе CLI-аргументов.
Эффект сегодня безвреден (неизвестный --variants= молча игнорируется
valueArg), но это ровно тот класс утверждения, который ТЗ обязано не
допускать: техническая деталь подана как проверенный факт, не будучи
сверенной с кодом инструмента. Правка — убрать --variants=60 из вызовов
benchmark:large-house-isometric/-interaction, оставить только у
benchmark:glow.
Medium-3 — четыре обязательных раздела §7.1 отсутствуют без пометки «не применимо»
Файл: docs/specs/473-iso-perf-witnesses-and-smoke.md (весь документ, разделы 1-10)
PROCESS.md §7.1 перечисляет обязательные разделы ТЗ: сценарий · что человек увидит до и после · проблема · скоуп и не-скоуп · контракт поведения · UX · модель данных и миграция · i18n · критерии приёмки · план автотестов · риски · откат · release-артефакты. В документе есть Проблема (§1), Скоуп/Не-скоуп (§2-3), контракт (§4-5), откат (внутри §6), критерии приёмки (§7), release-артефакты (§8) — но полностью отсутствуют как разделы: Сценарий, Что человек увидит до и после, UX/модель данных/i18n, Риски. Не встречается даже фраза «не применимо» — разделы просто не написаны.
Прямой прецедент — docs/specs/404-smoke-exception-guard.md (та же
инфраструктурная категория, тот же полный трек, тот же автор): там есть
## Сценарий (кто и когда столкнётся — разработчик и CI, не пользователь),
## Что человек увидит до и после (явно «видимого поведения продукта
задача не меняет»), ## UX, модель данных, i18n (одной строкой «не
применимо»), и отдельный ## Риски с тремя названными рисками и
смягчениями. Здесь этот минимум не выполнен.
Содержательно часть материала для «Рисков» в тексте есть, но рассыпана по другим разделам без общего заголовка: рост времени job (§10, как предположение с планом отката, а не как риск), достижимость мутации AABB (§10). Не названы вовсе: шумность абсолютного порога на 3 образцах на разделяемом раннере (ложный красный на чужой, легитимной правке — именно то, против чего заведена вся задача #473, доведённая до абсурда: диффозависимый смок может начать шуметь на легитимных изменениях и стать тем самым «гейтом не там, где нужно», о котором вторая половина Проблемы), и хрупкость четырёх новых мутантов к будущим рефакторингам подписи кэша. Раздел «Риски» обязателен не как формальность, а чтобы этот перечень стал видимым до, а не после инцидента.
Что проверено и корректно
- Числа и ссылка на прогон в §1 совпадают дословно с телом issue #473.
- Все построчные ссылки §4 на
iso-scene-render.ts(783, 786, 805, 821) иiso-overlays.ts(342-345) точны на текущем дереве. - Мутация AABB (
boundsNearбезgapи без равенства) описывает реальный код:boundsNearиспользует нестрогие<=/>=с допускомgap— мутация в строгие операторы без допуска валидна и достижима. - Предметы кэширования (WeakMap по
input.wallSilhouettes, подписьshapeSignature/signature, гард!previous.nearWallBefore || …) — описаны верно и совпадают с кодом дословно, вплоть до имён полей. budgets-glow-smoke.json/budgets-space-glow-smoke.json— уже существующий прецедент «smoke-бюджет как урезанное подмножество полного», подтверждает реализуемость §5 без нового механизма.hardMaxMs: 3500вbudgets-large-house-isometric.json(firstStableRenderMs) против измеренных 9870 мс в Проблеме — AC6 действительно ловится с одним образцом, как заявлено.de215578существует в истории — AC6 воспроизводимо.test/validate-workflow.test.mjs,test/performance-budget.test.mjsсуществуют и уже проверяют YAML/JSON текстовыми утверждениями — AC3-AC5 реализуемы заявленным способом (статический контракт, не запуск CI).scripts/gate-reuse.mjs: ключ переиспользования — хеш содержимого (sourceFingerprint+ оснастка job), не зависит от диапазона диффа; добавление признака набора профилей в ключ (AC5) технически сводится к добавлению строки вkey:кэша вvalidate.yml, без изменения самого скрипта — реализуемо, хотя способ (какая именно строка) оставлен автору как техническая деталь (правомерно, не продуктовый вопрос).- Пути
src/live-*,src/render-*,houseplan-render-lifecycle.ts,houseplan-card.ts,src/iso-*— все существуют, шаблоныhas()из Проблемы issue корректны. - Обоснование полного трека (не
small): две поверхности (тесты продуктового кода + workflow), перф — часть контракта задачи — корректно дисквалифицирует лёгкий трек по критерию §5 PROCESS.md. docs/specs/README.md: строка добавлена в верном месте таблицы, формат совпадает с соседними записями.- Не-скоуп (§3) дословно соответствует «Не входит» issue: пороги полных профилей и §11.4 не переоткрываются.
Чего не проверял
- Не прогонялись
typecheck/test/build/bundle:sync— дифф раунда не содержит ни продуктового, ни тестового кода, гонять нечего; это станет предметом код-ревью. - Не проверялась фактическая укладываемость трёх новых профилей в
20-минутный лимит
performance_smoke(§10, «Принятые предположения») — это требует реального прогона CI, которого на этапе ТЗ ещё нет; ТЗ само помечает это как предположение с планом отхода («лимит поднимается»), что корректно по формату §7.1. - Не проверялась фактическая шумность абсолютных порогов на 3 образцах на разделяемых GitHub-раннерах (см. Medium-3) — это открытый риск, а не проверяемый факт на этой стадии.
- Не читались все 122+ существующих мутанта в
scripts/mutation-gate.mjsпострочно — только формат записи и подтверждение, что регистрация новых идёт тем же путём. - Не проверялась точная фикстура «три комнаты, одна стена, плита у стены и плита вдали» на предмет буквального совпадения с какой-то одной существующей — это техническая деталь реализации, не продуктовый вопрос, и ревьюер вправе спросить о ней при коде, если фикстура не даст нужных условий.
Материал раунда
- SHA ветки:
2cedcc6221e40ea6ead2f48ff7fb37ddc96a51ca(= HEAD на момент ревью) - Дерево ТЗ:
docs/specs/473-iso-perf-witnesses-and-smoke.mdна этом SHA git show 2cedcc62 --stat:docs/specs/473-iso-perf-witnesses-and-smoke.md(new, 125 lines),docs/specs/README.md(+1 line)
Материал раунда
- Ветка:
issue/473-iso-perf-witnesses, коммит2cedcc6221e4— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
41b642d61cff58dea0718d19efa6209b897a5a33git log --all --format='%H %T' | grep 41b642d61cff - ТЗ
docs/specs/473-iso-perf-witnesses-and-smoke.md, блоб03c7d24cb503bea7ac083ed9a1ffc12a77b3c0e0git log --all --find-object=03c7d24cb503bea7ac083ed9a1ffc12a77b3c0e0 -- docs/specs/473-iso-perf-witnesses-and-smoke.md