Files
houseplan-card/docs/reviews/CODE-REVIEW-548-r1.md
T
2026-09-13 08:12:59 +00:00

18 KiB
Raw Blame History

CODE-REVIEW-548-r1

Issue: #548 — CI: TS/Python parity геометрии должна исполняться на чистом backend runner, а не skip Трек: инфраструктурный (§1 AGENTS.md/PROCESS.md) — ни одного файла класса A в диапазоне Материал: git log --oneline origin/dev..HEAD, git diff origin/dev...HEAD SHA материала: b15d357fa2d9b4596864905008be82c7239f47cf (HEAD ветки issue/548-backend-parity, рабочая копия проверена на нём — git rev-parse HEAD совпадает) Заход: r1 · блокирующих циклов израсходовано 0 из 4 (первый заход этапа code-review; предыдущий прогон конвейера на 76d8017e остановился на красном Validate до чтения кода и цикл не потратил — комментарий issue от 2026-09-13T07:56:49Z)

Скоуп

Два коммита:

  1. 76d8017e — основная реализация: отдельная reusable job geometry_parity в validate.yml, узкий tsconfig.junction-parity.json, новый исполняемый fail-closed харнесс tests_backend/junction_parity.py, перенос 15 сценариев из старого optional pytest-теста в test/fixtures/junction-limits-parity.json, удаление старого test_parity_with_the_frontend_checks (тихий skip), новый tests_backend/test_junction_parity.py, обновление classify-changes.mjs, check-inputs.mjs, gate-reuse.mjs, ci-proof.mjs, новый мутационный свидетель junction-limit-ts-python-parity-drift, правки документации (AGENTS.md, PROCESS.md, docs/ARCHITECTURE.md, docs/DEVELOPMENT.md).
  2. b15d357f — точечный фикс: evaluateCiProof не принимал reuse-заявку geometry_parity («unsupported proof mode»), найдено предыдущим (несостоявшимся) прогоном Validate с мутантами; добавлен geometry_parity в разрешённый список reuse-режимов и регрессионный тест на верифицированный источник.

Файлы только классов B/C/D-адъютант (test/**, tests_backend/**, scripts/**, .github/workflows/validate.yml, tsconfig.junction-parity.json, docs/**, AGENTS.md, PROCESS.md) — ни одного src/** или custom_components/**/*.py продуктового файла. Инфраструктурный трек применён корректно.

Продуктовая геометрия не менялась (заявлено автором и подтверждено чтением: src/junction-limits.ts и custom_components/houseplan/junction_limits.py отсутствуют в диапазоне изменённых файлов).

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

Гейт Статус Как
npx tsc --noEmit green прогнан локально на SHA b15d357f
npm test (полный) green не перегонял — уже подтверждён зелёным Validate на этом SHA (см. ниже); вместо этого прогнал целевой поднабор
Целевой node --test (ci-proof, check-inputs, classify-changes, gate-reuse, validate-workflow, mutation-gate) green, 129/129 node --test test/ci-proof.test.mjs test/check-inputs.test.mjs test/classify-changes.test.mjs test/gate-reuse.test.mjs test/validate-workflow.test.mjs test/mutation-gate.test.mjs
npm run build + сверка копий бандла не гонял diff не касается src/**/rollup.config.mjs; бандл не мог измениться, и это уже подтверждено зелёным Validate на точном SHA
node scripts/check-docs.mjs не гонял, не нужен diff не касается src/**
node scripts/model-invariants.mjs не гонял, не нужен геометрия/ссылки на неё не менялись — менялся только тестовый харнесс вокруг уже существующих зеркал
node scripts/smoke-select.mjs --base origin/dev --head HEAD прогнан «Исполняемого frontend-диффа нет — Browser-smoke этим диффом не выбираются». Смоки не нужны
python -m pytest tests_backend -q не гонял (pytest не установлен в среде ревьюера, .venv-backend не провизирован) вместо этого руками воспроизвёл обе новые проверки test_junction_parity.py вызовом их тела через python3 -c (см. «Защитные AC») и реальным прогоном tests_backend/junction_parity.py в отдельном git worktree
node scripts/process-gate.mjs --issues green (1 WARN, ожидаемый) «инфраструктурный диапазон (#562): статусная метка не требуется до S7-code-review»
Реальный прогон новой job на чистом runner воспроизведено на живом CI см. ниже

Дешёвые гейты уже подтверждены зелёным Validate на этом SHA: https://github.com/Matysh/houseplan-card/actions/runs/34746690349 (типы/юниты/сборка/бандл-синхрон/no-new-any) — поэтому полный npm test/npm run build не перегонялись, бюджет раунда потрачен на чтение кода и целевые прогоны.

Живая проверка на реальном CI (не из отчёта автора)

Проверил напрямую через gh run view, а не поверил хендоффу:

  • Run 34746521147 (SHA b15d357f): job «Геометрия: TS/Python parity исполнена» реально выполнилась (не reuse) — npm ci, tsc -p tsconfig.junction-parity.json, fix-test-build.mjs, python tests_backend/junction_parity.py --build-dir=test-build/junction-parity, все шаги green. Лог шага «Сравнить настоящие TS/Python validators»:
    geometry parity: 15 scenarios executed; TS/Python verdicts identical
    
    Это прямое доказательство AC1/AC4 — не «verified» без команды, а конкретный прогон с конкретным выводом.
  • Run 34746690349 (тот же SHA, повторный push-триггер): job geometry_parity — conclusion: skipped из-за reuse (тот же контент уже проверен), а job «Доказательство выполненных проверок» (proof) — success. Это подтверждает вживую, что фикс b15d357f (принятие geometry_parity в списке reuse-режимов evaluateCiProof) реально работает на настоящем прогоне, а не только в юнит-тесте.

Защитные AC — таблица «чем краснеет» (§2.7)

AC Чем доказан Чем краснеет
AC1: чистый runner исполняет parity реально; отсутствие prerequisites — setup failure, не skip tests_backend/test_junction_parity.py::test_missing_typescript_build_is_a_setup_failure_not_a_skip + реальный прогон job на CI (лог выше) Воспроизвёл сам: python3 -c с вызовом frontend_rules(spaces, build_dir=<несуществующий путь>) → FileNotFoundError: geometry parity prerequisites are missing: …, не pytest.skip. Старый test_parity_with_the_frontend_checks с pytest.skip(...) физически удалён (diff подтверждён чтением)
AC2: намеренное расхождение одного TS/Python правила красит guard Мутант junction-limit-ts-python-parity-drift в scripts/mutation-gate.mjs, guard = npx tsc -p tsconfig.junction-parity.json && node scripts/fix-test-build.mjs && python3 tests_backend/junction_parity.py --build-dir=test-build/junction-parity Воспроизвёл сам в отдельном git worktree (не в рабочей копии материала): применил патч MIN_NODE_DISTANCE_CM = 5 → 4, собрал TS через tsc -p tsconfig.junction-parity.json, прогнал junction_parity.py — получил AssertionError: TypeScript/Python geometry parity diverged: {"distance-4": {"typescript": [], "python": ["distance"]}}, exit code 1. Совпадает буквально с тем, что заявил автор в хендоффе («обнаружено на сценарии distance-4») — я это не переписал с его слов, а получил тем же прогоном независимо. Worktree удалён после проверки, рабочая копия чистая
AC3: selection/reuse учитывают обе стороны, fixture, runtime/pins test/classify-changes.test.mjs («#548: parity выбирается по обоим зеркалам…»), test/check-inputs.test.mjs («§8.1 представители» + «§8.1 обратная проба») Прогнал: node --test test/classify-changes.test.mjs test/check-inputs.test.mjs — green. Тест на обратную пробу явно проверяет, что src/houseplan-card.ts и custom_components/houseplan/websocket_api.py НЕ входят в manifest geometry_parity
AC4: результат CI явно показывает исполненный сценарий, а не только число тестов Job печатает geometry parity: N scenarios executed; TS/Python verdicts identical; ci-proof.mjs требует точное имя job Геометрия: TS/Python parity исполнена (JOB_RULES.geometry_parity) Подтверждено логом реального прогона (см. выше) — вывод буквально такой
Reuse-заявка geometry_parity не проходит без верифицированного источника (фикс b15d357f) test/ci-proof.test.mjs: «#548: geometry parity reuse is accepted only with a verified source job» Прогнал: тест сам содержит отрицательную пробу (reuseRuns: new Map() → status === 'failed') — воспроизводить руками нечего, тест уже параметризован обоими исходами и я убедился, что оба ветвления присутствуют и осмысленны

Проверено чтением, не исполнением

  • Точность миграции всех 15 сценариев из старого inline pytest-теста в test/fixtures/junction-limits-parity.json: построчно сверил каждый кейс (включая тонкие — debris-node с сырым -1e-8 в units, collinear-fork с units_offset: [0, 1e-9] поверх cm-точки) с оригинальными Python-выражениями cm(...)/ray(...) в удалённом коде tests_backend/test_junction_limits.py (версия origin/dev). Расхождений не нашёл — числа воспроизведены байт-в-байт по семантике, не только по количеству кейсов.
  • tsconfig.junction-parity.json компилирует только src/junction-limits.ts как root, но TS транзитивно подтягивает и эмитит space-geometry.ts через реальный import (import { GRID_STEP_N } from './space-geometry') — подтверждено и структурой tsconfig, и тем, что job реально получает оба файла в test-build/junction-parity/ (лог CI-прогона это показывает: harness падал бы на FileNotFoundError, если бы space-geometry.js не собрался, а он собрался).
  • scripts/fix-test-build.mjs не менялся содержательно (просто уже обходит всё дерево test-build/ рекурсивно) — новый выходной каталог test-build/junction-parity/ подхватывается без правок скрипта.
  • Не тронут backend-job (обычный HA pytest) — его needs/шаги не изменились; требование issue «не возвращать всему backend зависимость от всего UI ради одного теста» выполнено буквально: geometry_parity — отдельная job с needs: [changes, reuse], без зависимости от frontend.
  • Trailer-дисциплина: оба коммита несут Issue: #548 и User-Visible: no; changelog не тронут — корректно, поведение продукта не менялось.
  • process-gate.mjs --issues — green, единственный WARN ожидаемый (инфраструктурный трек без S* до первого code-review, что уже не актуально — метка S7-code-review уже стоит).

Находки

Нет. High: 0, Medium: 0, Low: 0.

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

  • Полный npm test и npm run build целиком — не гонял, положился на зелёный Validate на точном SHA (https://github.com/Matysh/houseplan-card/actions/runs/34746690349), дополнительно прогнав целевой поднабор (129 тестов) руками.
  • python -m pytest tests_backend -q целиком — в среде ревьюера нет pytest и нет .venv-backend (он провизируется только для облачных агентов). Вместо полного прогона: (а) сверил зелёный backend-job на предыдущих прогонах ветки не требовался для этого диапазона, так как правки в tests_backend/** не меняют HA-специфичные тесты; (б) вручную выполнил тела обеих новых функций test_junction_parity.py через python3 -c — обе ведут себя как заявлено.
  • Полный набор мутантов (scripts/mutation-gate.mjs целиком, все ~256 мутантов) — не гонял, это ночной/relase-гейт, а не гейт ревью (§8). Один целевой мутант (junction-limit-ts-python-parity-drift) воспроизведён руками персонально.
  • Golden/скриншоты/perf-смоки — diff не касается src/**/визуального вывода, не применимо.
  • npm run model-invariants — diff не меняет геометрию, только тестовый харнесс поверх неизменных зеркал; неприменимо.

Вердикт

Зелёный. Задача узкая, полностью в скоупе issue, инфраструктурный трек применён верно (ни одного файла класса A), все четыре AC issue доказаны — три из них я лично воспроизвёл (позитивный сценарий на живом CI, негативный сценарий через собственноручную мутацию в отдельном worktree, классификацию через целевые юнит-тесты), а не принял со слов автора. Отдельный точечный фикс b15d357f исправляет ровно то, что назвал предыдущий (нерасходующий цикл) прогон Validate, и сам доказан тестом с положительной и отрицательной ветками.


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

  • Диапазон: origin/dev..HEAD, коммиты 76d8017e, b15d357f.
  • SHA материала ревью: b15d357fa2d9b4596864905008be82c7239f47cf.
  • Дерево рабочей копии на момент подведения итогов совпадает с git rev-parse HEAD на момент начала разбора — новых коммитов в ходе ревью не появилось.

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

  • Ветка: issue/548-backend-parity, коммит b15d357fa2d9 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: e062bddd5488d8aef8049c726d85fedee1ea2a8e
    git log --all --format='%H %T' | grep e062bddd5488
    
  • Тело issue: 583f18e1a114a5eaab171e7394d7c74ddcb51ca9741beb2e9126afefc27f44bb
  • Вердикт конвейера: green · High 0