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

158 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](https://github.com/Matysh/houseplan-card/actions/runs/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](https://github.com/Matysh/houseplan-card/actions/runs/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`
на момент начала разбора — новых коммитов в ходе ревью не появилось.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/548-backend-parity`, коммит `b15d357fa2d9` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `e062bddd5488d8aef8049c726d85fedee1ea2a8e`
```
git log --all --format='%H %T' | grep e062bddd5488
```
- Тело issue: `583f18e1a114a5eaab171e7394d7c74ddcb51ca9741beb2e9126afefc27f44bb`
- Вердикт конвейера: `green` · High 0