Files
houseplan-card/docs/reviews/CODE-REVIEW-671-r1.md
T
2026-09-27 06:02:34 +00:00

13 KiB
Raw Blame History

CODE-REVIEW-671-r1

Issue: #671 — «Пакет ассетов (assets/furniture/**, assets/fonts/**) не входит ни в один manifest Validate и выпадает из ключа переиспользования». Материал раунда: 05dfcd1c8afe3c13495b8333755a013d7a45fdec (ветка issue/671-assets-input-manifest, ровно один коммит над origin/dev@09873251). Рабочая копия проверена — HEAD детачнут ровно на этом SHA, дерево чистое. Класс изменения: B (инфраструктура/гейты) — scripts/check-inputs.mjs, scripts/mutation-registry.mjs, test/check-inputs.test.mjs, docs/TESTING.md. Ни одного файла класса A не тронуто — трек инфраструктурный, спецификации/спек-ревью не было, что соответствует правилу «infrastructure-only» (AGENTS.md). Трейлеры коммита: Issue: #671, User-Visible: no — корректно: изменение не меняет наблюдаемое пользователем поведение продукта, только манифест CI-проверок. Changelog не тронут — это правильно при User-Visible: no.

Скоуп

Диапазон git log --oneline origin/dev..HEAD: один коммит 05dfcd1c ci: guard asset inputs in Validate manifest.

git diff origin/dev...HEAD --stat:

docs/TESTING.md               |  8 ++++----
scripts/check-inputs.mjs      | 26 ++++++++++++++++++++------
scripts/mutation-registry.mjs | 22 ++++++++++++++++++++++
test/check-inputs.test.mjs    | 29 ++++++++++++++++++++++++++++-
4 files changed, 74 insertions(+), 11 deletions(-)

Три AC из тела issue (раздел «Ожидаемое»):

# AC Чем доказан Чем краснеет
AC1 assets/furniture/** — точный вход frontend (и всего, что реально читает пакет) Прочитан код: CHECKS.frontend.roots теперь содержит 'assets/furniture/**' (scripts/check-inputs.mjs:336); тест test/check-inputs.test.mjs «#671: весь пакет мебели…» подтверждает вложенный путь assets/furniture/houseplan-0.4.1/svg/menu/air_conditioner.svg попадает в MANIFEST.frontend, checksAffectedBy для этого файла даёт ровно {affected: ['frontend'], unknown: []}. Перепрогнан лично: node --test --test-name-pattern="#671: весь пакет мебели" test/check-inputs.test.mjs → зелёный Мутант furniture-assets-dropped-from-frontend-inputs (scripts/mutation-registry.mjs), применён вручную к рабочей копии и откачен: после удаления 'assets/furniture/**' из roots тест краснеет (модуль перестаёт парситься — SyntaxError, гайдовая логика mutation-guard-outcome.mjs намеренно засчитывает такой крэш как ASSERTION_KILLED: «a test that executes the mutated path and crashes has still exposed the regression», это документированное поведение репозитория, не находка)
AC2 assets/fonts/** — вход либо запись в NOT_AN_INPUT с причиной NOT_AN_INPUT содержит ['assets/fonts/**', 'исходный TTF читает только ручной generate-pdf-font.mjs; Validate использует закоммиченный результат'] (scripts/check-inputs.mjs:89); проверено, что оба tracked-файла (assets/fonts/Roboto-Regular.ttf, assets/fonts/LICENSE) реально покрываются глобом — node scripts/check-inputs.mjs --coverage не печатает ни неизвестный вход, ни NOT_AN_INPUT лишний, exit 0 Тот же тест «#671: весь пакет мебели…» проверяет isDeclaredNotAnInput(font) и MANIFEST.frontend.has(font) === false; ручной прогон подтвердил зелёный
AC3 (защитный) Тест, который краснеет, если assets/** снова выпадет из manifest GUARDED_DATA_ROOTS = ['assets'] + isGuardedInput() встроены в checksAffectedBy и coverage() (scripts/check-inputs.mjs:40,414-417,428,443); тест «#671: новый assets/** без владельца…» заводит синтетический assets/future-pack/new.bin и проверяет, что он одновременно (а) расширяет affected до полного набора и (б) попадает в coverage().unknown Мутант assets-data-root-not-guarded (GUARDED_DATA_ROOTS = []) применён вручную и откачен: тест падает настоящим AssertionError (deepStrictEqual ожидал ['assets/future-pack/new.bin'], получил []) — чистое доказательство, не крэш

Все три AC подтверждены чтением кода и личным исполнением тестов/мутаций (не только доверием к комментарию автора).

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

Мутации к scripts/check-inputs.mjs применялись через python3/прямую замену строки, идентичную patch.find/patch.replace из реестра (тот же механизм, что applyPatches в scripts/mutation-execution.mjs: source.replace(patch.find, patch.replace) — сверено чтением scripts/mutation-execution.mjs:18-27), затем откатывались из бэкапа; git status после каждого прогона — чисто.

Гейты — что прогнано и почему

Гейт Статус Комментарий
typecheck, npm test, npm run build, bundle-policy --verify не перегонялись Validate на этом же SHA 05dfcd1c уже зелёный (https://github.com/Matysh/houseplan-card/actions/runs/36298246002) — дешёвые гейты подтверждены, перегонять незачем (issue #343)
node --test test/check-inputs.test.mjs test/classify-changes.test.mjs test/gate-reuse.test.mjs прогнан лично 56/56 зелёных — совпадает с числом из комментария автора
node scripts/check-inputs.mjs --coverage прогнан лично exit 0, вывод пуст — ни «неизвестный вход», ни «NOT_AN_INPUT лишний»
Оба защитных мутанта (furniture-assets-dropped-from-frontend-inputs, assets-data-root-not-guarded) прогнаны лично вручную (не через полный mutation-gate.mjs, дешевле и достаточно для проверки конкретных двух свидетелей) Оба реально краснеют; см. таблицу AC
node scripts/smoke-select.mjs --base origin/dev --head HEAD прогнан «Исполняемого frontend-диффа нет (src/**/*.ts не тронут)», смоки не выбираются — ожидаемо для CI-only диффа
node scripts/check-docs.mjs не прогонялся правило требует его только при диффе по src/**; диф src/** не трогает
golden:verify не прогонялся нет видимого пользователю изменения (User-Visible: no, диф не в src/**)
pytest tests_backend не прогонялся ни один .py-файл в диффе не изменён
npm run invariants не прогонялся диф не трогает геометрию или ссылки на неё
performance-профили не прогонялись не названы ни в AC, ни в issue
полный mutation-gate.mjs/весь набор npm test не прогонялся целиком покрыт зелёным Validate на этом SHA; проверены точечно только два новых мутанта и три файла тестов, прямо относящиеся к AC

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

  • assets/furniture/** объявлен точным входом frontend, включая вложенные пути реального пакета (houseplan-0.4.1/svg/menu/...) — проверено исполнением, не только чтением.
  • Все читатели пакета мебели, названные в теле issue (test/furniture-assets.test.mjs, test/furniture-path-join.test.mjs, test/bundle-assets.test.mjs, scripts/generate-furniture-assets.mjs), лежат либо под test/** (уже корень frontend), либо под scripts/** (уже исполняемый вход по EXECUTABLE_ROOTS) — отдельно добавлять их не требовалось, и авторская фраза «читатели уже покрыты test/**» подтверждена grep по дереву.
  • Утверждение автора «у frontend нет reuse, поэтому сырой пакет не должен попадать в reuse-ключи smoke/golden/performance» — проверено чтением: CHECKS.frontend.reuse отсутствует, а assets/furniture/** не встречается ни в одном другом roots/entries (grep -n assets scripts/check-inputs.mjs — только frontend и объявления самих корней). Значит generated TS остаётся единственным мостом к browser-job, как и заявлено.
  • assets/fonts/** — явное NOT_AN_INPUT с причиной, глоб реально покрывает оба tracked-файла (Roboto-Regular.ttf, LICENSE), не оставляя «дыр» и не будучи избыточным.
  • Защитный AC3 — не «тест умеет падать» без мутации: обе мутации реестра лично применены и откачены, оба результата зафиксированы (крэш модуля для первого — документированно засчитываемый исход этого репозитория; AssertionError для второго).
  • docs/TESTING.md обновлён синхронно с кодом: новое определение «неизвестный охраняемый вход» и строка про assets/furniture/**/assets/fonts/** соответствуют фактическому коду.
  • Трейлеры коммита корректны, changelog не тронут — согласуется с User-Visible: no.
  • Ни одно число, видимое пользователю, в диффе не появляется — раздел §8 «одно число — один источник» неприменим к этому диффу.

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

  • Полный npm run gate:small и весь npm test не перегонялись целиком — доверился зелёному Validate на точном SHA материала (ссылка в задании ревью), перепроверив только файлы и мутанты, прямо относящиеся к AC.
  • Ночной полный реестр мутаций (mutation-gate.yml) не запускался — вне гейта ревью, это предрелизный/ночной прогон.
  • Не проверял поведение scripts/gate-reuse.mjs на реальном CI-раннере (реюз ключей для smoke/golden/performance_smoke) — только статически подтвердил, что assets/furniture/** не входит в их roots/entries.
  • Не проверял, что удаление шрифта (assets/fonts/**) как класса «no-op» действительно ловится существующим generate-pdf-font.mjs вручную — вне скоупа задачи (эта часть уже существовала до issue #671, задача только документирует и относит её к NOT_AN_INPUT).

Находки

Нет. High: 0, Medium: 0, Low: 0.

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

05dfcd1c8afe3c13495b8333755a013d7a45fdec — рабочая копия на этом SHA, git status чист, git rev-parse HEAD совпадает.


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

  • Ветка: issue/671-assets-input-manifest, коммит 05dfcd1c8afe — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 1eee15c0ca6ef4021b49d678f45fe8d51ca9e4c5
    git log --all --format='%H %T' | grep 1eee15c0ca6e
    
  • Тело issue: b0e013e0e77d8ad15970dca3f8a6b43bf19bb76a38076f522aa6d206aa652b12
  • Вердикт конвейера: green · High 0