# SPEC-REVIEW-425-r1 - Issue: https://github.com/Matysh/houseplan-card/issues/425 - ТЗ: `docs/specs/425-core-file-budget.md`, ветка `issue/425-core-file-budget`, SHA `4eae3398a6510ea19b403fd1ac33a5a3cc44881b` (единственный коммит поверх `origin/dev`, класс C: `docs: specify the core file budget gate (#425)`, `Issue: #425` · `User-Visible: no`). - Заход: r1 · трек: полный (снят с `small` автором — «метка small конвейер не запускает»; техническая причина смены трека вне моей компетенции, к содержанию ТЗ отношения не имеет). - Вердикт: **жёлтый**. ## Скоуп задачи Ядро #425: заменить #34 узким, автоматизируемым ограничителем — гейт-храповик на размер `src/houseplan-card.ts` и `src/houseplan-editor-runtime.ts` (растёт выше потолка → красный тест; падает больше чем на 250 строк ниже потолка → тоже красный, чтобы потолок опускали, а не забывали). Только тестовая инфраструктура: один тест, один мутант, строка в `docs/TESTING.md`, закрытие #34. Продуктового кода, UX, i18n, миграций конфига задача не касается (`User-Visible: no`). ## Как проверялось 1. `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` — прочитаны целиком. 2. Тело issue #425 и оба комментария (переход на `small`, затем откат на полный трек) — прочитаны через `gh issue view 425 --json ...` (MCP-доступ к `get_issue`/`get_issue_comments` не выдан в этой сессии). 3. Тело ТЗ прочитано целиком (`docs/specs/425-core-file-budget.md`, 103 строки). 4. Проверено по факту, а не на слово: - `#34` существует, открыт, `S3-spec` — ссылка и статус в ТЗ не выдуманы; - текущие размеры ядер: `wc -l src/houseplan-card.ts` → 13658, `src/houseplan-editor-runtime.ts` → 14322 — совпадают с колонкой `dev` в таблице ТЗ день в день; - заявленные «42% всего TypeScript» — при подсчёте строками выходит 38.97% (27980/71793), но при подсчёте байтами (как считает `bundle-budget.mjs` — прецедент похожего гейта в этом же репозитории) — 42.66% (1312569/3076509 Б по `src/**/*.ts` на `origin/dev`). Автор посчитал байтами; расхождение с интуитивным «строками» — не ошибка, а другая единица измерения, оставляю как проверенный факт; - `scripts/mutation-gate.mjs`, `test/mutation-gate.test.mjs`, `.github/workflows/mutation-gate.yml` существуют — приём «тест сопровождается мутантом в `scripts/mutation-gate.mjs`» это не догадка, а действующая конвенция репозитория (`docs/TESTING.md`, правило 4); - `scripts/bundle-budget.mjs` — единственный похожий по духу гейт в репозитории — проверен на предмет пересечения: он про initial-view gzip бандла, не про строки исходника; дублирования функциональности нет; - `test/*.test.mjs` — гейта на размер файлов исходников сегодня нет (`grep` по «budget/потолок» ничего похожего не находит) — задача действительно новая, а не повтор существующего. 5. `node scripts/process-gate.mjs` на диапазоне `origin/dev..HEAD` — «гейт пройден, предупреждений 0» (класс C, трейлеры корректны, веткование верно). 6. Продуктового кода коммит не содержит (`git diff origin/dev...HEAD --stat` — один файл, `docs/specs/425-core-file-budget.md`), поэтому дешёвые гейты (`typecheck`/`test`/`build`) к материалу этого раунда неприменимы — они отвечают на вопрос «работает ли код», а кода в этом раунде нет. Зелёный Validate на `4eae3398` (ссылка в контексте задачи) это подтверждает попутно. ## Находки ### Medium-1 — нет обязательных разделов «Сценарий» и «Что человек увидит до и после» (§7.1) `docs/specs/425-core-file-budget.md`, весь документ. PROCESS.md §7.1 называет эти два раздела первыми не случайно: «ТЗ, которое не может ответить на эти два вопроса, описывает работу, а не изменение продукта». В документе их нет вообще — ни в явном виде, ни как «не применимо» (для секции «UX, модель данных, i18n» автор такую отметку сделал: строка 62 — «Не применимо: продуктового кода задача не касается»; для Сценария и «что увидит человек» — не сделал). **Сценарий воспроизведения:** открыть файл и поискать заголовки «Сценарий» и «Что человек увидит» — их нет; DoR-чеклист §2.5 требует полный набор разделов §7.1 перед переводом в `S5-ready`. Задача действительно не имеет пользовательской видимости (`User-Visible: no`, гейт CI), поэтому по существу ответ тривиален — но именно поэтому его нужно написать явно, тем же приёмом, что уже применён к UX-разделу: «Сценарий: не применимо — персона отсутствует, единственный наблюдатель контракта — разработчик, читающий вывод `npm test`». Без этой строки документ не проходит формальный чеклист §2.5, хотя по сути вопрос закрыт. Серьёзность: Medium, в скоупе — правится добавлением двух коротких абзацев, без изменения контракта. ### Medium-2 — AC4 (и частично AC5) не называют способ доказательства `docs/specs/425-core-file-budget.md:45-46`. DoR (§2.5) требует у каждого AC «указано, чем он доказывается: unit / backend / smoke / golden / «ревью кода»». AC1–AC3 это делают явно («Доказательство: мутант...», «Доказательство: тот же юнит»). AC4 — нет: > **AC4**. Потолки записаны числами в самом тесте: любое их изменение видно в > дифе и проходит ревью как решение, а не как побочный эффект. Способ доказательства здесь по смыслу — «ревью кода» (структурное свойство: константа лежит в тесте, а не читается из внешнего конфига), но текст этого не говорит, и план автотестов (строки 70–75) AC4 не упоминает вовсе — только AC1, AC2, AC3, AC5. AC5 хотя бы косвенно закрыт пунктом 5 плана («потолки заданы для обоих ядер и ни для чего больше (AC5)»); у AC4 такой связи нет ни в тексте AC, ни в плане. **Как проявится, если не поправить:** на код-ревью придётся решать самому, чем считать AC4 доказанным — юнитом или чтением кода; при полном треке это дало бы почву для расхождения между автором и ревьюером кода. Серьёзность: Medium, в скоупе — правится одной строкой («Доказательство: ревью кода — константы `...` лежат в теле теста, изменение видно в diff»). ### Low-1 — раздел «Откат» задублирован `docs/specs/425-core-file-budget.md:48-50` и `:94-96` — два одинаковых раздела `## Откат` с идентичным текстом «Удаление одного тестового файла и одного мутанта.» (первый явно перенесён из тела issue, второй добавлен при расширении до полного трека, старый не убран). Содержательного расхождения нет, ambiguity не создаёт. Снимаю с записью: чинится удалением одного из двух блоков при следующей правке ТЗ, отдельного цикла не требует. ## Что проверено и корректно - **Замена #34.** Ссылка живая, статус (`S3-spec`, открыт) соответствует описанию «за 25 дней не сдвинулся»; заявление не выдумано. - **Числа в таблице долга** (13658 / 14322 на `dev`) сверены с деревом напрямую — точное совпадение день в день. - **AC1–AC3, AC5** — однозначны, каждый привязан к конкретному проверяемому условию и (кроме AC5 частично) к явному способу доказательства; план автотестов на чистой функции `coreBudgetViolations(sizes, caps, slack)` описывает все ветки, включая обе границы (строка 74: «границы включительно») — асимметрия «рост без люфта / падение с люфтом 250 строк» прочитана из контракта верно и без противоречий. - **Мутант** соответствует действующей конвенции `docs/TESTING.md` (правило 4): 2–5-строчный патч в реальный файл, а не в тест, тест обязан покраснеть. - **Не найдено ни одной догадки, выданной за факт.** Все технические утверждения (существование `scripts/mutation-gate.mjs`, конвенция мутантов, текущие размеры файлов, отсутствие похожего гейта) проверены по дереву репозитория, а не приняты на слово. - **Открытых продуктовых вопросов владельцу нет** — и не должно быть: задача целиком техническая, ни одного вопроса о том, что видит или делает человек, в ней нет. - **`docs/USER-GUIDE.ru.md` и канонические документы подсистем** (`SUN.md`, `LIGHT.md`, `CANVAS.md`, `WALL-THICKNESS.md`, `UX-MODES.md`, `CONFIG-COMPATIBILITY.md`, `TOUCH-SUPPORT.md`) не относятся к этой задаче — видимое поведение не меняется, интерфейсная терминология не используется; проверено тем, что задача помечена `User-Visible: no` и раздел UX ТЗ прямо это фиксирует. - **`docs/SCOPE.md` / Core user jobs.** Задача не закрывает ни одну строку J1–J7 — и не обязана: это не продуктовая фича, а инженерный гейт качества (`User-Visible: no`, класс B/C по DoD), аналогичный уже принятым в этом же репозитории `scripts/bundle-budget.mjs` (#337/#367) и самому `scripts/mutation-gate.mjs` (#85). SCOPE.md ограничивает, что становится видимой функцией продукта; техдолг разработки под этот ограничитель не подпадает. Это не находка, а явно проверенное и подтверждённое отсутствие конфликта. - **Класс изменений и трейлеры коммита r1** — `process-gate.mjs` подтвердил автоматически (см. «Как проверялось», п.5). ## Чего не проверял и почему - **Дешёвые гейты кода** (`npx tsc --noEmit`, `npm test`, `npm run build`, сверка бандла) — не прогонял: материал раунда не содержит продуктового или тестового кода, только текст ТЗ; прогонять их не на чем. - **Мутационный гейт, golden, смоки, инварианты модели, perf-профили, бэкенд-тесты** — не прогонял: код гейта ещё не написан (это стадия ТЗ, не `S6`/`S7`), диф не трогает `src/**`, геометрию, рендер, Python или touch. Ни один из этих гейтов не относится к материалу этого раунда по diff'у или AC. - **`docs/specs/README.md`** — не требую внесения записи об issue #425/#34 в этот индекс: свежие специфицированные задачи (#422, #416) там тоже не зарегистрированы, реестр по факту не поддерживается синхронно (см. и собственную пометку PROCESS.md §7.3 п.1 о его частичном устаревании), это не критерий приёмки ни этой, ни соседних задач. - **Точность исторических чисел** (18 файлов на v1.58, 20287 строк на v1.66.0) — не перепроверял по тегам: это фон мотивации, не AC, ошибка в нём не меняет проверяемость контракта. ## Материал раунда - Ветка: `issue/425-core-file-budget` - SHA: `4eae3398a6510ea19b403fd1ac33a5a3cc44881b` - ТЗ: `docs/specs/425-core-file-budget.md` (единственный файл в диффе с `origin/dev`) ## Вердикт Жёлтый: обе находки Medium в скоупе задачи, High нет. Обе правятся точечно — двумя короткими разделами и одной строкой доказательства у AC4 — без изменения контракта, AC или объёма задачи. Low снят с записью, отдельного действия не требует. `Вердикт: жёлтый · заход r1 · блокирующих циклов 0/4 · High: 0 · Medium: 2 → в задаче · Документ: docs/reviews/SPEC-REVIEW-425-r1.md` --- ## Материал раунда - Ветка: `issue/425-core-file-budget`, коммит `4eae3398a651` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `d37bea6a940c33f8197d96b008432931ad7edb8f` ``` git log --all --format='%H %T' | grep d37bea6a940c ``` - ТЗ `docs/specs/425-core-file-budget.md`, блоб `213aaf2a92ffc550f9fd87d2b4b81338a82359a6` ``` git log --all --find-object=213aaf2a92ffc550f9fd87d2b4b81338a82359a6 -- docs/specs/425-core-file-budget.md ```