Files
houseplan-card/docs/reviews/CODE-REVIEW-425-r1.md
T
2026-09-02 21:51:06 +00:00

14 KiB
Raw Blame History

CODE-REVIEW-425-r1

  • Issue: https://github.com/Matysh/houseplan-card/issues/425
  • Ветка: issue/425-core-file-budget, материал: git diff origin/dev...HEAD на SHA d4ecace6
  • ТЗ: docs/specs/425-core-file-budget.md (полный трек), спек-ревью зелёное на r2 (docs/reviews/SPEC-REVIEW-425-r2.md)
  • Заход: r1 · блокирующих циклов израсходовано 0 из 4

Скоуп

Диапазон origin/dev..HEAD — 5 коммитов:

4eae3398 docs: specify the core file budget gate (#425)
3df0f164 docs: review document for #425
84b5f63c docs: address the core budget spec review (#425)
f399ad54 docs: review document for #425
d4ecace6 test: cap the two frontend cores with a ratchet (#425)

Единственный коммит с продуктовым воздействием на гейты — d4ecace6:

docs/TESTING.md                 | 14 ++++++
scripts/mutation-gate.mjs       | 13 +++++
test/core-file-budget.test.mjs  | 109 ++++++++++++++++++++++++++++++

Остальные — спек-документы и ревью-документы предыдущего этапа (класс C), к код-ревью не относятся содержательно. Диф не трогает src/**, custom_components/**, demo/** — класс B (гейты и инструменты), issue переиспользован как разрешено таблицей классов. User-Visible: no на каждом коммите корректно: продуктовый код не менялся.

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

Гейт Команда Результат
typecheck npx tsc --noEmit зелёный, 0 ошибок
unit (полный) npm test 1778 тестов, 1777 pass, 1 skip (issue 281 private exact fixture... — существующий условный skip, не связан с #425), 0 fail
build + сверка копии npm run build && cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js бандл собран, побайтовое совпадение; копия demo/srv/assets/** не коммитится (#255) и не сравнивается
целевой тест node --test test/core-file-budget.test.mjs 7/7 pass
мутант (штатный раннер) node scripts/mutation-gate.mjs --id=core-budget-ignores-growth ok core-budget-ignores-growth: тест покраснел, как обязан — поймано 1 из 1
проверка якорей мутанта node scripts/mutation-gate.mjs --check ok core-budget-ignores-growth — патч применим к текущему коду
выбор браузерных смоков node scripts/smoke-select.mjs --base origin/dev --head HEAD «Исполняемого frontend-диффа нет (src/**/*.ts не тронут). Browser-smoke этим диффом не выбираются — выбирать нечего.»
process-gate (офлайн) node scripts/process-gate.mjs --base origin/dev --head HEAD «гейт пройден, предупреждений 0»
фактические размеры ядер node -e "…split('\n').length" на обоих файлах houseplan-card.ts = 13659, houseplan-editor-runtime.ts = 14323 — совпадает с CAPS в тесте

Чего не проверял и почему

  • node scripts/check-docs.mjs — не запускал: диф не трогает src/**, условие «любой diff по src/** требует check-docs» не выполнено.
  • Полный прогон node scripts/mutation-gate.mjs (все мутанты) — не гонял: дорогой прогон, задача трогает ровно один новый мутант, который проверен адресно через --id. Попутно замечено (не находка этой задачи): полный прогон в этом окружении падает на несвязанном бэкенд-мутанте из-за отсутствующего pytest (No module named pytest) — окружение без Python- венды, не регрессия #425.
  • golden:verify, smoke_*.mjs, pytest tests_backend, performance-профили — диф не меняет визуал, DOM, Python-код или производительность; AC их не называют. smoke-select.mjs независимо подтвердил «выбирать нечего».
  • node scripts/model-invariants.mjs — диф не касается геометрии, толщины стен, layout, marker.space, open_spans.
  • Ручное тестирование в браузере — задача не создаёт видимого поведения (User-Visible: no, продуктовый код не тронут), пункт неприменим по определению задачи.

Разбор по AC

  • AC1 (рост ядра выше потолка роняет npm test с числом). Доказано исполнением, не чтением: мутант core-budget-ignores-growth (scripts/mutation-gate.mjs:936-948) добавляет 400 строк в src/houseplan-card.ts перед export class HouseplanCard, что больше люфта (250). Прогон node scripts/mutation-gate.mjs --id=core-budget-ignores-growth дал «тест покраснел, как обязан» — тест реально умеет падать, а не только умеет проходить. Текст сообщения называет файл, потолок и превышение (test/core-file-budget.test.mjs:41-47), проверено юнитом «рост выше потолка становится нарушением с числом» (строки 67-72).
  • AC2 (уменьшение больше чем на 250 строк тоже роняет тест, с требованием опустить потолок). Доказано юнитом «заметное уменьшение требует опустить потолок» (:74-79): проверяет kind==='shrank', under===300, текст содержит «Опустите потолок». Чтением подтверждено, что тот же путь исполнения используется и на реальных ядрах (measure вызывается один раз в верхнем тесте, coreBudgetViolations — чистая функция без ветвления по источнику данных).
  • AC3 (изменение в пределах люфта тест не трогает). Доказано юнитами «изменение в пределах люфта не трогает никого» и «границы включительно» (:81-92) — отдельно проверены точки cap, cap-slack, cap+1, cap-slack-1, то есть обе границы включительно и оба соседних шага исключительно.
  • AC4 (потолки — числа в тесте, не вычисление от текущего размера). Доказательство по ТЗ — «ревью кода плюс юнит на то, что функция принимает потолки аргументом». Юнит «потолки — числа в этом файле…» (:99-109) парсит блок const CAPS из исходника теста и проверяет regex-отсутствие measure|readFileSync|process.env внутри блока — то есть механически исключает как раз тот способ обмана, который ТЗ называет неприемлемым (потолок, вычисленный от текущего размера). Прочитано чтением: CAPS (:21-24) — действительно литерал из двух чисел, coreBudgetViolations (:33-57) получает caps и slack параметрами и не читает файлы и env сама; чтение файлов инкапсулировано в отдельной measure (:59), вызываемой только в интеграционном тесте верхнего уровня. Проверено чтением, не исполнением, в части «архитектурного» инварианта (что функция не может прочитать файл сама), исполнением — в части regex-проверки текста.
  • AC5 (гейт судит только два ядра, остальные файлы не ограничены). Доказано юнитом «потолки заданы для двух ядер и ни для чего больше» (:94-97), сверяющим точный список ключей CAPS. Чтением подтверждено, что других мест в src/**, ссылающихся на CAPS/coreBudgetViolations, нет (единственный потребитель — сам файл теста).

Все пять AC доказаны: AC1 — мутантом, исполненным лично (не на слово автора); AC2/AC3/AC5 — юнитами, прогнанными лично; AC4 — комбинацией юнита и чтения кода с явной пометкой, какая часть доказана как.

Соответствие ТЗ и процессу

  • Числа CAPS (13659, 14323) совпадают с фактическим размером файлов на этом SHA, измеренным независимо (split('\n').length), что и есть мера, описанная в комментарии над CAPS (:14-16) и совпадающая с мерой в measure() (:59) — единственный источник числа, нет второго измерения той же величины другим способом (правило «одно число — один источник» неприменимо буквально, т.к. величина инженерная и не видна пользователю, но принцип соблюдён: потолок и измерение используют одну и ту же функцию подсчёта строк).
  • docs/TESTING.md дополнен разделом с объяснением, что делать при красном гейте — соответствует Release-артефактам ТЗ (только TESTING.md, changelog не требуется при User-Visible: no).
  • Расхождение чисел в теле issue (таблица «dev: 13658») с CAPS («13659») — не дефект: таблица в issue использует wc -l (мера issue), тест — мера split('\n').length; автор явно проговорил это расхождение в хендофф- комментарии и держит одну меру в коде, что и требовалось. Само число в тексте issue — историческая иллюстрация, не контрактное значение.
  • Скоуп ТЗ («один тест с потолками и храповиком, мутант, строка в docs/TESTING.md») выдержан ровно, попутных правок нет.
  • SCOPE.md: задача инженерная, User-Visible: no, не обязана закрывать Core user job — прецедент (#337 bundle-budget, #85 mutation-gate) уже принят ревью ТЗ на r1, не переоткрываю.

Находки

Нет находок High или Medium. Low не обнаружено сверх уже снятого на этапе ТЗ.

Одна стилистическая заметка, не являющаяся дефектом (не заводится ни как Low, ни как issue): пункт docs/TESTING.md:3593 оформлен чек-боксом - [ ], хотя проверка полностью автоматическая и уже входит в npm test — соседние чек-боксы в этом файле смешивают ручные и автоматические проверки, так что формат не расходится с текущей конвенцией документа.

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

  • Ветка: issue/425-core-file-budget
  • SHA на момент вывода: d4ecace6 (сверено непосредственно перед подведением итогов, см. git rev-parse HEAD в начале сессии — совпадает с HEAD диапазона origin/dev..HEAD, использованного во всех командах выше)
  • Дерево материала: полный диф origin/dev...HEAD, 6 файлов изменено (см. таблицу выше)

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

  • Ветка: issue/425-core-file-budget, коммит d4ecace64c51 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 226386b38b1cb661fcb45b2c8f01a568b954a70a
    git log --all --format='%H %T' | grep 226386b38b1c
    
  • ТЗ docs/specs/425-core-file-budget.md, блоб 0469a1eec26759469cf469c87136556935a1f629
    git log --all --find-object=0469a1eec26759469cf469c87136556935a1f629 -- docs/specs/425-core-file-budget.md