docs: review document for #425

Issue: #425
User-Visible: no
This commit is contained in:
claude[bot]
2026-09-02 21:51:06 +00:00
parent d4ecace64c
commit 52333c73d0
+169
View File
@@ -0,0 +1,169 @@
# 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 файлов изменено (см.
таблицу выше)
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `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
```