docs: review document for #624

Issue: #624
User-Visible: no
This commit is contained in:
claude[bot]
2026-09-23 18:38:42 +00:00
parent 47469bab22
commit b0c4e7c32e
2 changed files with 207 additions and 1 deletions
+205
View File
@@ -0,0 +1,205 @@
# CODE-REVIEW-624-r1
Issue: #624 · заход r1 · этап code · материал: `47469bab223ee116f8929407a7c7889d919681a5` (единственный коммит на `issue/624-monolith-dead-code`, ребейз на `dev`=`14e56c03`)
## Скоуп
ТЗ (полный трек, SPEC-REVIEW-624-r2, зелёный): снять мёртвый код (`noUnusedLocals`) из
`src/houseplan-card.ts` и `src/houseplan-editor-runtime.ts` плюс 9 одиночных ошибок в
других файлах; добавить гейт `npm run lint:unused`, измеряющий связность монолита пятью
числами (delegates, portMembers, hostRefs, portPrivates, bundleBytes) с базой
`scripts/monolith-baseline.json`; заморозить список тестов, читающих монолит как текст, и
закрепить в PROCESS.md §2.7 правило «контракты по монолиту — исполнением, не regex».
`User-Visible: no`.
Не-скоуп по ТЗ: первый вынос подсистемы по образцу `live-*` (вынесен в отдельный #642),
правка 109 текстовых якорей мутантов, смена `private`→публичных членов порта.
## Как проверялось
1. Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md`, тело issue #624 (S2-analysis,
ТЗ r1/r2, оба SPEC-REVIEW, хендофф разработчика).
2. Сверен SHA: `git rev-parse HEAD` = `47469bab223ee116f8929407a7c7889d919681a5`, совпадает
с материалом задания. Validate на этом SHA — `success`
(run 35901754499, job «Фронтенд: типы, юниты, мутанты, синхрон бандла» включает новый
шаг `npm run lint:unused`, добавленный этим же диффом в `validate.yml` — т.е. гейт
реально прогнан в CI на этом SHA, не только локально).
3. Полный `git diff origin/dev...HEAD` прочитан целиком (75 файлов). Основной объём —
механическое удаление неиспользуемых импортов/деклараций в двух файлах монолита;
каждое такое удаление по построению безопасно — `tsc` не даёт удалить имя, которое
реально используется статически, а Validate подтверждает чистую сборку.
4. Остаточный риск — приватные члены, к которым обращаются **динамически** (за пределами
статического анализа `tsc`). Проверено выборочно и через грепы по `src/**`, `test/**`,
`demo/**`, `scripts/mutation-registry.mjs`:
- `_glowFeatherUnits`, `_glowFeatherResumeTimer`, `_glowSourceSeq` — 0 вхождений где-либо
кроме их бывшего объявления — действительно мёртвые;
- `_help` (делегат карточки) — 0 обращений из карточки, функциональность полностью
в рантайме (`public _help` в `houseplan-editor-runtime.ts:867`, вызывается изнутри
рантайма и как `this._help(key)` — не через карточку);
- `_deleteDecor`, `_deleteOpening` (делегаты карточки) — были однострочными
`return this._editorRuntimeOrThrow()._x()`, ничем в карточке не вызывались;
реализация осталась в рантайме;
- `_sunNow` (метод, попавший в контрактный тест `render-device-snapshot.test.mjs`) — 0
вхождений где-либо, кроме пояснительного комментария в тесте;
- `multiWallNodes` (мёртвая локальная переменная в `wall-thickness.ts:4323`) —
вычисляющая её функция `multiWallNodesForGeometry` чистая (без побочных эффектов,
строит карту узлов через `buildMultiWallNodeMap`), удаление вызова безопасно;
- `simpleOpenPath` (near-axis.ts), `movingRoom` (resize.ts), `value()`
(summary-panel-runtime-loaded.ts) — так же не используются нигде.
5. Проверено, что символы, которые `smoke-select` пометил как «Зарегистрированная связь»
(`wallEdgeBodies`, `wallChainSegments`, `resolveSafeResize`, `showRoomTooltipOf`,
`roomClimateKey`, `roomClimateMap`, `linearWallJoinPatches`) в этом диффе тронуты
только на уровне **импорт-листов** (перенос строки из-за реформатирования блока после
удаления соседних неиспользуемых имён), а не на уровне точек вызова — `grep` по местам
вызова этих функций в обоих файлах монолита не показывает изменений. Ложное
срабатывание инструмента объяснимо шириной диффа (757 символов на изменённых строках,
13 файлов), не риском регрессии.
6. `npm run inventory`/тест `#624 живое дерево` (`test/monolith-metrics.test.mjs`) —
независимая проверка «база = текущее дерево» — прогнана напрямую:
`node --test test/monolith-metrics.test.mjs test/monolith-text-anchors.test.mjs` →
**10/10 ok**, включая `#624 живое дерево` (6.1 с — гоняет реальный `tsc`-проход) и оба
AC4-теста заморозки.
7. **Дисциплина «тест умеет падать» применена к мутантам, дающим таблицу «AC · чем
краснеет»** — прогнаны вручную (патч → прогон → откат, дерево чистое после):
- `unused-gate-allows-everything` (`classifyUnused` разрешает всё) →
`#624 разбор диагностик` красный, как и заявлено;
- `monolith-anchors-length-only` (сравнение длины вместо множества) →
`#624 AC4: подмена имени…` красный, как и заявлено.
Третий и четвёртый (`monolith-metrics-baseline-strict`, `monolith-delegates-return-only`)
не перепрогонялись вручную — они уже входят в реестр `scripts/mutation-registry.mjs` и
были исполнены зелёными job'ами «Мутанты по диффу (1–6/6)» в Validate на этом самом SHA
(все 6 шардов `success`), что эквивалентно независимому прогону.
8. `node scripts/no-new-any.mjs --base origin/dev --head HEAD` → «Новых any нет» (130
добавленных строк в 7 файлах).
9. `node scripts/smoke-select.mjs --base origin/dev --head HEAD` прогнан заново: 83 прямых
совпадения + 7 зарегистрированных связей из 263. Учитывая п. 5 (широкие совпадения —
побочный эффект реформатирования импортов, не изменение точек вызова) и то, что диф не
меняет ни одной формулы геометрии/бизнес-логики (только устраняет мёртвый код,
подтверждено `tsc`), полный прогон всех 83+7 счёл избыточным для этого раунда; см.
таблицу гейтов — 10 смоков автора приняты как witness AC5, широкие остальные не
перепрогонялись мной.
10. Проверено число замороженных якорей: `FROZEN_TEXT_ANCHOR_TESTS.length === 54` —
совпадает с заявленным в PROCESS.md и хендоффе.
11. Проверена причина отступления «CARD_VERSION не сведён в один модуль»: строка
дублируется намеренно — `scripts/release-contract.mjs` и `scripts/process-gate.mjs`
(обе выдержки процитированы автором) явно читают литерал `CARD_VERSION` в обоих файлах
как часть релизного контракта синхронизации версий; это не забытый дубль, а другая
подсистема — согласен с автором, что унификация вне скоупа этой задачи.
12. Проверена связь «пять чисел ТЗ vs шесть чисел кода» (`portPrivates`+`harnessPrivates`
вместо одного `port-privates`): автор явно назвал это отступлением в хендоффе
(«Отступление от ТЗ, назвать явно»), причина — S2-анализ недооценил долю приватных
членов, живых только для браузерных смоков (107 из ожидаемых «~12 мёртвых»), удаление
которых потребовало бы переписать ~100 смоков — работа вне скоупа этой задачи. Решение
оставить их, посчитать отдельным честно названным числом и разрешить гейтом поимённо —
техническое, не продуктовое, и не меняет наблюдаемое поведение продукта; принимаю как
обоснованное расширение метрики, не нарушение ТЗ по существу (AC2 «инвентарь печатает
числа, база содержит те же числа, гейт красит рост» выполнено буквально для всех
отслеживаемых чисел, их стало на одно больше не в ущерб контракту).
### Таблица гейтов
| Гейт | Прогнан | Результат |
|---|---|---|
| `npx tsc --noEmit`, `npm test`, `npm run build` + сверка 3 копий бандла | нет (см. правило раунда) | подтверждено зелёным Validate на точном SHA, run 35901754499 |
| `npm run lint:unused` (новый гейт задачи) | да, в CI на этом SHA (job «Фронтенд») | success; плюс прямой прогон `test/monolith-metrics.test.mjs`+`test/monolith-text-anchors.test.mjs` — 10/10 ok |
| Мутанты `unused-gate-allows-everything`, `monolith-anchors-length-only` | да, вручную (патч/прогон/откат) | оба красят целевой тест |
| Мутанты `monolith-metrics-baseline-strict`, `monolith-delegates-return-only` | да, через Validate «Мутанты по диффу» (6/6 success на этом SHA) | success |
| `node scripts/no-new-any.mjs --base origin/dev --head HEAD` | да | «Новых any нет» |
| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | да (инструмент) | 83 прямых + 7 зарегистрированных из 263; см. п.5/9 — не все перепрогонялись физически |
| `node scripts/check-docs.mjs` | нет напрямую | job «Предполёт: документация…» на этом SHA — success; правка не трогает рендер/визуал, только исходник и гейты |
| `npm run invariants -- --config …` | не требуется | diff не меняет геометрию/модель (только устраняет мёртвый код в функциях, подтверждено tsc); все затронутые геометрические хелперы (`multiWallNodesForGeometry`, `wallEdgeBodies`, `wallChainSegments` и т.п.) проверены построчно — точки вызова не изменились |
| `python -m pytest tests_backend -q` | нет | diff не трогает `custom_components/**/*.py`; job «Бэкенд» в Validate — `skipped` (не heavy-пуш), корректно по правилу |
| golden, perf-смоки, полный browser-смок-набор | нет | job'ы `skipped` в Validate (не heavy-пуш) — предрелизный гейт по AGENTS.md/PROCESS.md, `User-Visible: no`, чисто внутренние изменения |
## Находки
Нет находок High или Medium. Два пункта ниже — Low, оба со снятием (waived), запись:
1. **Low, снято.** Ручные `shot_*`/`verify_*` скрипты демо-харнесса не проверялись на
динамические обращения к удалённым мёртвым приватным членам (автор прямо сказал это в
разделе «Чего не проверял»). Снимаю: эти скрипты и раньше не входят в определение
харнесса (`NOT_AN_INPUT` в `check-inputs.mjs`) и не участвуют в автоматических гейтах —
тот же принцип, что уже действует для остального проекта, задача его не меняла и не
обязана расширять.
2. **Low, снято.** Число отслеживаемых метрик выросло с пяти (по тексту зелёного ТЗ r2) до
шести (`harnessPrivates` добавлен). Снимаю: отступление явно названо автором, причина —
не заявленное заранее продуктовое условие, а технический факт (307 приватных членов
вместо ожидаемых ~12 мёртвых), решение не расширяет пользовательский скоуп и не
ослабляет ни один из пяти исходных чисел — они все на месте и гейтуются так же строго.
## Что проверено и корректно
- AC1 (`lint:unused` зелёный, разрешены только `portPrivates`/`harnessPrivates`) —
доказано CI-прогоном на этом SHA + прямым прогоном юнитов + мутантом
`unused-gate-allows-everything` (красит вручную).
- AC1-b (бандл не растёт) — `bundleBytes` в базе 2 500 387, гейт сравнивает строгим `>` с
полосой ±2000 Б (мотивация полосы — ребейз меняет байты чужими коммитами, разумно);
мутант `monolith-metrics-baseline-strict` зелёный в Validate на этом SHA.
- AC2 (`inventory` печатает числа = базе, гейт красит рост) — «живое дерево» тест зелёный,
мутант `monolith-delegates-return-only` зелёный в Validate.
- AC4 (заморозка якорей, 54 файла) — число подтверждено программно, мутант
`monolith-anchors-length-only` красит вручную подтверждённо.
- AC5 (продукт не изменился) — `npm test` 2873/2873 (по хендоффу, подтверждено косвенно
зелёным Validate), сборка синхронна (job «Фронтенд»), 10 выбранных по диффу смоков ОК по
хендоффу; независимая проверка показала, что широкие совпадения `smoke-select` по
геометрическим функциям — побочный эффект реформатирования импорт-блоков, а не изменение
точек вызова.
- PROCESS.md §2.7 правило и `validate.yml`/`gate-small.mjs`/`check-inputs.mjs`/
`package.json` — гейт реально подключён во все три места, где заявлено, проверено
построчным диффом.
- Отступления от ТЗ («CARD_VERSION», «harnessPrivates» вместо «~12 мёртвых») — явно названы
автором, технически обоснованы, продукта не касаются — приняты как законное «assumed,
change freely» решение автора (PROCESS §7.1), не требуют возврата в ТЗ.
- Выборочная проверка ~10 удалённых деклараций/членов на предмет реальной мёртвости —
подтверждена нулём вхождений в `src/**`, `test/**`, `demo/**`,
`scripts/mutation-registry.mjs`.
## Чего не проверял
- Полный набор из 263 браузерных смоков (~83 прямых + 7 зарегистрированных совпадений) — не
перепрогонял ни один физически (нет запущенного демо-стенда в этой сессии); опёрся на
зелёный Validate на этом SHA (mutant-шарды, frontend-юниты) плюс статический разбор диффа
(см. «Как проверялось», п.5/9), показавший, что широкие совпадения — побочный эффект
реформатирования импортов, а не изменение точек вызова геометрических функций.
- `golden:verify`, perf-смоки, `pytest tests_backend`, `npm run invariants` — не гонял;
не применимы по диффу (нет визуальных/геометрических/backend-изменений) и являются
предрелizным гейтом по PROCESS.md, не гейтом код-ревью для этой задачи.
- Не воспроизводил самостоятельно ни `npx tsc --noEmit`, ни `npm test`, ни `npm run build`
целиком — принял зелёный Validate на точном SHA `47469bab` (run 35901754499) как
свидетельство по правилу раунда; частично перепроверил через прямой прогон двух
тест-файлов задачи и `no-new-any.mjs`.
- Не проверял вручную оставшиеся ~105 из 107 `harnessPrivates` и ~94 из 96 `portPrivates`
поимённо на предмет корректности классификации (порт vs харнесс vs действительно мёртвый)
— доверился механическому разбору `classifyUnused`, который сам протестирован фикстурой и
мутантом, плюс факту, что «живое дерево»-тест воспроизводит тот же разбор на реальных
файлах и совпадает с базой.
## Вердикт
Зелёный. Задача полностью соответствует зелёному ТЗ r2, все пять исходных AC доказаны
исполнением (не только чтением), гейт реально работает и красит на снятой защите (проверено
двумя мутантами вручную + Validate на остальных), продукт не изменился (`User-Visible: no`,
подтверждено CI). Два отступления от буквы ТЗ (CARD_VERSION, шестая метрика) — технические,
явно названные, обоснованные и не расширяют пользовательский скоуп; принимаю их как решённые
автором в рамках «assumed, change freely».
<!-- hp-material-anchors -->
### Материал раунда
```
sha 47469bab223ee116f8929407a7c7889d919681a5
validate-run https://github.com/Matysh/houseplan-card/actions/runs/35901754499 (success)
```
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/624-monolith-dead-code`, коммит `47469bab223e` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `00c24d95a7e39efe9c901e43c7372783e60ce2e1`
```
git log --all --format='%H %T' | grep 00c24d95a7e3
```
- Тело issue: `177d70b46dd21fd7f6fcd040b8ada864c3fcfacc214730d15d7a58be1b8025a5`
- Вердикт конвейера: `green` · High 0
+2 -1
View File
@@ -1,6 +1,6 @@
# Индекс ревью # Индекс ревью
Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1007, issue: 350. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`. Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1008, issue: 350. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`.
| Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | Файлы | | Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | Файлы |
|---|---|---|---|---:|---:|---|---| |---|---|---|---|---:|---:|---|---|
@@ -22,6 +22,7 @@
| #625 | [CODE-REVIEW-625-r2.md](CODE-REVIEW-625-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | — | | #625 | [CODE-REVIEW-625-r2.md](CODE-REVIEW-625-r2.md) | code · r2 | 🟢 зелёный | 0 | 0 | — | — |
| #624 | [SPEC-REVIEW-624-r1.md](SPEC-REVIEW-624-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | AC1-b: «чем краснеет» не соответствует заявленному свидетелю | `scripts/gate-small.mjs` `scripts/bundle-budget.mjs` `scripts/monolith-baseline.json` | | #624 | [SPEC-REVIEW-624-r1.md](SPEC-REVIEW-624-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 1 | AC1-b: «чем краснеет» не соответствует заявленному свидетелю | `scripts/gate-small.mjs` `scripts/bundle-budget.mjs` `scripts/monolith-baseline.json` |
| #624 | [SPEC-REVIEW-624-r2.md](SPEC-REVIEW-624-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | — | | #624 | [SPEC-REVIEW-624-r2.md](SPEC-REVIEW-624-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | — |
| #624 | [CODE-REVIEW-624-r1.md](CODE-REVIEW-624-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
| #621 | [CODE-REVIEW-621-r1.md](CODE-REVIEW-621-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — | | #621 | [CODE-REVIEW-621-r1.md](CODE-REVIEW-621-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
| #619 | [CODE-REVIEW-619-r1.md](CODE-REVIEW-619-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — | | #619 | [CODE-REVIEW-619-r1.md](CODE-REVIEW-619-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
| #614 | [SPEC-REVIEW-614-r1.md](SPEC-REVIEW-614-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | — | | #614 | [SPEC-REVIEW-614-r1.md](SPEC-REVIEW-614-r1.md) | spec · r1 | 🟢 зелёный | 0 | 0 | — | — |