21 KiB
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→публичных членов порта.
Как проверялось
- Прочитаны
docs/SCOPE.md,AGENTS.md,PROCESS.md, тело issue #624 (S2-analysis, ТЗ r1/r2, оба SPEC-REVIEW, хендофф разработчика). - Сверен SHA:
git rev-parse HEAD=47469bab223ee116f8929407a7c7889d919681a5, совпадает с материалом задания. Validate на этом SHA —success(run 35901754499, job «Фронтенд: типы, юниты, мутанты, синхрон бандла» включает новый шагnpm run lint:unused, добавленный этим же диффом вvalidate.yml— т.е. гейт реально прогнан в CI на этом SHA, не только локально). - Полный
git diff origin/dev...HEADпрочитан целиком (75 файлов). Основной объём — механическое удаление неиспользуемых импортов/деклараций в двух файлах монолита; каждое такое удаление по построению безопасно —tscне даёт удалить имя, которое реально используется статически, а Validate подтверждает чистую сборку. - Остаточный риск — приватные члены, к которым обращаются динамически (за пределами
статического анализа
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) — так же не используются нигде.
- Проверено, что символы, которые
smoke-selectпометил как «Зарегистрированная связь» (wallEdgeBodies,wallChainSegments,resolveSafeResize,showRoomTooltipOf,roomClimateKey,roomClimateMap,linearWallJoinPatches) в этом диффе тронуты только на уровне импорт-листов (перенос строки из-за реформатирования блока после удаления соседних неиспользуемых имён), а не на уровне точек вызова —grepпо местам вызова этих функций в обоих файлах монолита не показывает изменений. Ложное срабатывание инструмента объяснимо шириной диффа (757 символов на изменённых строках, 13 файлов), не риском регрессии. 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-теста заморозки.- Дисциплина «тест умеет падать» применена к мутантам, дающим таблицу «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), что эквивалентно независимому прогону.
node scripts/no-new-any.mjs --base origin/dev --head HEAD→ «Новых any нет» (130 добавленных строк в 7 файлах).node scripts/smoke-select.mjs --base origin/dev --head HEADпрогнан заново: 83 прямых совпадения + 7 зарегистрированных связей из 263. Учитывая п. 5 (широкие совпадения — побочный эффект реформатирования импортов, не изменение точек вызова) и то, что диф не меняет ни одной формулы геометрии/бизнес-логики (только устраняет мёртвый код, подтвержденоtsc), полный прогон всех 83+7 счёл избыточным для этого раунда; см. таблицу гейтов — 10 смоков автора приняты как witness AC5, широкие остальные не перепрогонялись мной.- Проверено число замороженных якорей:
FROZEN_TEXT_ANCHOR_TESTS.length === 54— совпадает с заявленным в PROCESS.md и хендоффе. - Проверена причина отступления «CARD_VERSION не сведён в один модуль»: строка
дублируется намеренно —
scripts/release-contract.mjsиscripts/process-gate.mjs(обе выдержки процитированы автором) явно читают литералCARD_VERSIONв обоих файлах как часть релизного контракта синхронизации версий; это не забытый дубль, а другая подсистема — согласен с автором, что унификация вне скоупа этой задачи. - Проверена связь «пять чисел ТЗ 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), запись:
- Low, снято. Ручные
shot_*/verify_*скрипты демо-харнесса не проверялись на динамические обращения к удалённым мёртвым приватным членам (автор прямо сказал это в разделе «Чего не проверял»). Снимаю: эти скрипты и раньше не входят в определение харнесса (NOT_AN_INPUTвcheck-inputs.mjs) и не участвуют в автоматических гейтах — тот же принцип, что уже действует для остального проекта, задача его не меняла и не обязана расширять. - 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 test2873/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 на точном SHA47469bab(run 35901754499) как свидетельство по правилу раунда; частично перепроверил через прямой прогон двух тест-файлов задачи иno-new-any.mjs. - Не проверял вручную оставшиеся ~105 из 107
harnessPrivatesи ~94 из 96portPrivatesпоимённо на предмет корректности классификации (порт vs харнесс vs действительно мёртвый) — доверился механическому разборуclassifyUnused, который сам протестирован фикстурой и мутантом, плюс факту, что «живое дерево»-тест воспроизводит тот же разбор на реальных файлах и совпадает с базой.
Вердикт
Зелёный. Задача полностью соответствует зелёному ТЗ r2, все пять исходных AC доказаны
исполнением (не только чтением), гейт реально работает и красит на снятой защите (проверено
двумя мутантами вручную + Validate на остальных), продукт не изменился (User-Visible: no,
подтверждено CI). Два отступления от буквы ТЗ (CARD_VERSION, шестая метрика) — технические,
явно названные, обоснованные и не расширяют пользовательский скоуп; принимаю их как решённые
автором в рамках «assumed, change freely».
Материал раунда
sha 47469bab223ee116f8929407a7c7889d919681a5
validate-run https://github.com/Matysh/houseplan-card/actions/runs/35901754499 (success)
Материал раунда
- Ветка:
issue/624-monolith-dead-code, коммит47469bab223e— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
00c24d95a7e39efe9c901e43c7372783e60ce2e1git log --all --format='%H %T' | grep 00c24d95a7e3 - Тело issue:
177d70b46dd21fd7f6fcd040b8ada864c3fcfacc214730d15d7a58be1b8025a5 - Вердикт конвейера:
green· High 0