Files
houseplan-card/docs/reviews/CODE-REVIEW-399-r2.md
T
claude[bot] 6bf39ee9a7
Проверка (CI) / Классификация изменённых файлов (push) Successful in 20s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 33s
Проверка (CI) / HACS: валидация репозитория (push) Failing after 15s
Проверка (CI) / Предполётные проверки: документация, провенанс, процесс (push) Failing after 44s
Проверка (CI) / Hassfest: манифест интеграции (push) Failing after 17s
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Failing after 4m55s
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Failing after 5m59s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
docs: review document for #399
Issue: #399
User-Visible: no
2026-08-31 01:54:28 +00:00

161 lines
13 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-399-r2
- Issue: https://github.com/Matysh/houseplan-card/issues/399
- Этап: code (PROCESS.md §2.7)
- Заход: r2 · блокирующих циклов израсходовано 1/4
- HEAD: `5c4d8cba9ebb2815dd45f91641d39621435a1144` (детач на
`origin/issue/399-backend-gate-honesty`)
- Материал: `git log --oneline origin/dev..HEAD`,
`git diff origin/dev...HEAD`; для этого раунда — дельта
`git diff 98028a30..HEAD` относительно SHA предыдущего код-ревью
- ТЗ на входе: `docs/specs/399-backend-gate-honesty.md`, ревизия 3,
принята зелёным вердиктом SPEC-REVIEW-399-r3 (без изменений с r1)
## Дельта этого раунда
Предыдущий код-ревью (CODE-REVIEW-399-r1, документ в `8b2a146c`) прошёл
на `98028a3093713122d6dbc1b08fc830b730e8a2a3` с вердиктом жёлтый,
High: 1. Автор ответил единственным коммитом:
`5c4d8cba` — `test: prove AC5 by running the scanner, not the predicate (#399)`
`git diff 98028a30..HEAD` (без документов ревью, которые сами по себе
не код):
- `test/validate-workflow.test.mjs` — обход каталога вынесен в
экспортируемую функцию `pinViolations(directory)`; тест «AC5»
переписан: вместо вызова `installsPythonDeps` на строковых литералах
строится настоящий временный каталог из трёх файлов и вызывается
**та же** функция, что работает в проде.
- `scripts/mutation-gate.mjs` — мутант `workflow-scan-hardcodes-the-list`
меняет цель патча с прежней (`workflows = readdirSync(...)` →
список из одного имени) на новую (`files = readdirSync(...)` →
список из **двух настоящих** имён `['validate.yml', 'mutation-gate.yml']`).
Больше ничего не менялось: `pyproject.toml`, `tests_backend/requirements.txt`,
`test/backend-pins.test.mjs`, `test/lint-scope.test.mjs` дельтой не
затронуты. Рёбейза не было (`git merge-base origin/dev HEAD` = tip `dev`,
проверено). Delta локальна и меньше по объёму, чем исходная задача —
полный разбор не требуется, разбираю только то, до чего дотягивается
изменение: AC5 и связанный с ним мутант.
## Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| **[High]** Тест, названный AC5, вызывал `installsPythonDeps` на строковых литералах внутри теста и не исполнял реальный обход каталога (`readdirSync(WORKFLOWS).filter(...)`). Воспроизведение: откат к списку из двух реальных имён + подложенный третий workflow с непинованной установкой → 10/10 зелёных. | Обход вынесен в `pinViolations(directory)` — функцию с каталогом как параметром. Тест AC5 (`test/validate-workflow.test.mjs:259`) строит временный каталог из трёх файлов (пинованный `validate.yml`, безобидный `docs.yml`, `zz-rogue.yml` с непинованной установкой) и вызывает именно эту функцию, а не предикат в изоляции. Утверждает на возвращённых `scanned` (3), `installers` (2) и точном списке `problems` для `zz-rogue.yml`. | Воспроизвёл ровно сценарий r1 повторно на текущем HEAD: `sed` вернул обход к `['validate.yml', 'mutation-gate.yml']` внутри `pinViolations` → `node --test test/validate-workflow.test.mjs` даёт **9/10**, падает именно тест «#399 AC5: тот же код ловит третий workflow в подставном каталоге» (`ENOENT` на втором захардкоженном имени, которого нет во временном каталоге) — не побочный assert, а прямое доказательство, что тест исполняет реальный путь. Дерево вернул (`git checkout -- test/validate-workflow.test.mjs`), `git status --short` пуст. |
Мутант `workflow-scan-hardcodes-the-list` пересмотрен под замечание r1
о том, что прежняя форма (список из одного имени) ловилась по
посторонней причине (`assert.ok(workflows.length >= 2, ...)`), а не по
существу. Новая форма подставляет список из **двух настоящих** имён —
неотличимую от корректного кода на сегодняшнем дереве, то есть более
вероятную форму регрессии. Прогнал штатным раннером:
```
node scripts/mutation-gate.mjs --id=workflow-scan-hardcodes-the-list
ok чистый прогон: node --test test/validate-workflow.test.mjs
ok workflow-scan-hardcodes-the-list: тест покраснел, как обязан
поймано 1 из 1
```
Дерево после прогона чистое (`git status --short` пусто).
## Как проверялось (этот раунд)
Зелёного Validate на `5c4d8cba` нет — гейты прогнаны вручную. Диапазон
изменений — конфигурация CI/тестов, `src/**` не тронут ни в дельте, ни
в остальном диапазоне (см. r1).
- `npx tsc --noEmit` — чисто, exit 0.
- `npm test` — **1667/1667 pass, 1 skipped**, совпадает с заявленным
автором числом и с результатом r1 (delta не должна была изменить
общее число тестов — так и есть: -1 старый AC5-тест, +1 новый, чистый
ноль).
- `npm run build` — собрался (`tsc --noEmit && rollup -c`, ~16s).
Сверка трёх копий бандла не делалась: дельта не трогает `src/**`,
контент бандла не мог измениться.
- `node scripts/check-docs.mjs` — не прогонялся: дельта не трогает
`src/**` (условие запуска по инструкции не выполнено, как и в r1).
- Геометрические инварианты, `golden:verify`, browser-смоки,
`pytest tests_backend`, `ruff` — не по необходимости и не по
возможности среды: дельта не касается геометрии, рендера, продуктового
Python-кода и видимого поведения; причины идентичны r1 (нет
установленных `homeassistant`/`ruff` в песочнице, проверено `pip show`).
- Воспроизведение регрессии, которую AC5 обязан ловить (см. таблицу
выше) — прогнано лично на этом HEAD, не унаследовано с чужих слов.
- Мутант `workflow-scan-hardcodes-the-list` прогнан штатным раннером на
новой форме патча (см. выше).
## Что проверено и признано корректным
- **AC5** — теперь доказан исполнением: `pinViolations` — единственная
точка обхода каталога, используемая и продовым тестом на реальном
`.github/workflows/`, и AC5-тестом на синтетическом каталоге. Оба пути
проходят через один и тот же код, различие — только во входных данных.
Регрессия из r1 (список из двух имён) теперь красит именно AC5-тест,
без постороннего assert. Мутант заточен под ту же форму регрессии и
ловится.
- Синтетический каталог AC5-теста корректно воспроизводит оба вида
нарушения контракта одним файлом (`zz-rogue.yml` одновременно не
ставит из файла пинов и не версионирован) — соответствует двум
проверкам внутри `pinViolations`, порядок утверждений в
`assert.deepEqual` детерминирован (оба сообщения относятся к одному
файлу, порядок не зависит от порядка обхода каталога `readdirSync`).
Учёл это отдельно, так как порядок `readdirSync` не гарантирован
POSIX — но здесь он не влияет на исход теста, что делает тест
устойчивым к этой недетерминированности, а не случайно совпадающим.
## Унаследовано из r1 (без повторной проверки)
Дельта этого раунда не касается перечисленного — принимаю выводы
CODE-REVIEW-399-r1 (`8b2a146c`, HEAD того раунда `98028a3093713122d6dbc1b08fc830b730e8a2a3`)
как есть:
- **AC1** (пин фронтенда из констрейнтов HA) — подтверждён в r1 сетевым
источником (`gh api` к `home-assistant/core@2026.8.3`), файлы
`tests_backend/requirements.txt`, `test/backend-pins.test.mjs` дельтой
не тронуты.
- **AC2/AC3** (согласование `[tool.ruff] include` и шага линта в
`validate.yml`, `test/lint-scope.test.mjs`) — сверены в r1 построчно,
файлы дельтой не тронуты.
- **AC4** (позитивный признак `installsPythonDeps` вместо пропуска по
отсутствию подстроки) — сама функция `installsPythonDeps` не менялась
в этой дельте (менялась только функция, которая её использует —
`pinViolations`, и структура теста).
- **AC6** (гейты не деградировали) — подтверждён в r1 числом `npm test`;
в этом раунде число совпало снова, деградации нет.
- Мутанты `lint-scope-drifts`, `frontend-pin-drifts-from-ha` — прогнаны
и пойманы в r1, дельта их не затрагивает.
- Трейлеры `Issue: #399` / `User-Visible: no` на всех девяти коммитах
диапазона — перепроверены заново в этом раунде (см. «Как
проверялось»), не только унаследованы: результат идентичен r1.
- Low-наблюдение r1 про `pip3 install` / `python3 -m pip install`,
не распознаваемые `installsPythonDeps` — вне скоупа AC5 (тот описывает
текстовый `pip install`), дельта его не касается, остаётся
наблюдением без действия.
- Low-наблюдение r1 про склеенный текст в теле коммита `98028a30` —
тот коммит не переписывался, наблюдение остаётся снятым без действия.
## Чего не проверял
- `python -m pytest tests_backend`, `ruff check`, `mypy strict` — среда
не изменилась с r1, пакеты по-прежнему не установлены (не
перепроверял `pip show` повторно, так как дельта их не касается и
вывод не мог измениться).
- `npm run golden:verify`, geometry invariants, browser-смоки — не по
необходимости: дельта — только тестовый файл и файл мутантов,
видимое поведение продукта не существует для этой задачи вовсе.
- `node scripts/check-docs.mjs` — условие запуска (`src/**` в диффе) не
выполнено.
- Реальный HA-харнесс с новым пином фронтенда — ограничение среды,
идентичное r1, дельта его не касается.
## Вывод
High из r1 закрыт по существу и проверен воспроизведением, а не
заявлением автора: тест, названный AC5, теперь действительно исполняет
код обхода каталога и красится при возврате к хардкод-списку. Новых
находок в дельте нет. Задача может двигаться дальше без ещё одного
блокирующего цикла код-ревью.