mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-02 12:49:56 +00:00
docs: specify the backend gate honesty fixes (#399)
User-Visible: no Issue: #399
This commit is contained in:
Executable
+171
@@ -0,0 +1,171 @@
|
||||
# ТЗ #399 — Бэкенд-гейт проверяет ровно то, что обещает
|
||||
|
||||
- Issue: https://github.com/Matysh/houseplan-card/issues/399
|
||||
- Приоритет: P2, infra; полный трек — класс B (конфигурация гейта, workflow,
|
||||
тесты), три несвязанные поверхности в одном issue по решению аудита
|
||||
- Ревизия: 1 (2026-08-31)
|
||||
|
||||
## Сценарий
|
||||
|
||||
Разработчик смотрит на зелёный `backend` и делает вывод: «интеграция проверена
|
||||
против закреплённого Home Assistant, линт прошёл по объявленному скоупу,
|
||||
версии зафиксированы». Каждое из трёх утверждений сегодня чуть шире правды, и
|
||||
именно из-за таких зазоров #392 уже случился: харнесс полгода тихо проверял
|
||||
интеграцию против февральского HA, и ни один гейт об этом не сказал.
|
||||
|
||||
## Что человек увидит до и после
|
||||
|
||||
Пользователь продукта — ничего. Разработчик перестаёт получать три ложных
|
||||
обещания: пин фронтенда соответствует закреплённому HA, объявленный скоуп
|
||||
линта совпадает с проверяемым, а проверка пинов не может выключить сама себя.
|
||||
|
||||
## Проблема и контракты по пунктам
|
||||
|
||||
### (1) M3 — пин фронтенда не соответствует закреплённому HA
|
||||
|
||||
`tests_backend/requirements.txt:29` фиксирует
|
||||
`home-assistant-frontend==20260826.1`, тогда как рядом (`:32`) закреплён
|
||||
`homeassistant==2026.8.3`, а его `package_constraints.txt` в теге `2026.8.3`
|
||||
репозитория `home-assistant/core` требует `home-assistant-frontend==20260729.7`
|
||||
(проверено загрузкой файла). `pytest-homeassistant-custom-component` фронтенд
|
||||
в зависимостях не объявляет вовсе, то есть версия выбрана вручную и ничем не
|
||||
выведена.
|
||||
|
||||
Функционального отказа сегодня нет: пакет — статика, тесты его не исполняют.
|
||||
Но цель #392 формулировалась дословно как «по SHA видно, чем проверяли», а
|
||||
проверяется набор, которого не существует ни в одном релизе HA.
|
||||
|
||||
**Контракт**: версия фронтенда выводится из констрейнтов закреплённого HA, а
|
||||
не назначается. В комментарии рядом сказано, откуда она берётся, чтобы
|
||||
следующий подъём HA не превратился в угадывание.
|
||||
|
||||
### (2) Low «а» — ruff линтит уже, чем объявляет конфиг
|
||||
|
||||
`pyproject.toml:5` включает в `[tool.ruff] include` три дерева:
|
||||
`custom_components/houseplan/**/*.py`, `scripts/*.py`, `tests_backend/**/*.py`.
|
||||
Шаг «Линт бэкенда» (`.github/workflows/validate.yml:787-788`) исполняет
|
||||
`python -m ruff check custom_components/houseplan` — одно дерево из трёх.
|
||||
|
||||
Сужение было осознанным решением #42 (щадящий трек, «без массового rewrite»),
|
||||
и пересматривать само решение эта задача не обязана. Проблема в том, что
|
||||
конфиг об этом молчит: читающий `pyproject.toml` видит три дерева и делает
|
||||
неверный вывод. Наблюдаемое следствие — в `tests_backend/test_validation.py`
|
||||
проехали мёртвые `import sys`/`import types` (F401) и переопределения (F811),
|
||||
которых на v1.69.0 не было.
|
||||
|
||||
**Контракт**: объявленный скоуп и проверяемый совпадают. Допустимы два исхода,
|
||||
и оба честны — выбрать при реализации, замерив цену:
|
||||
|
||||
1. сузить `include` до реально проверяемого дерева, а расширение оставить
|
||||
отдельной задачей с разбором долга;
|
||||
2. расширить CI до полного `include`, разобрав накопленные находки
|
||||
(`ruff check custom_components/houseplan scripts tests_backend` — порядка
|
||||
полусотни, преимущественно I001/E402/F401 в тестах).
|
||||
|
||||
Решение фиксируется в комментарии рядом с `include`, чтобы следующий читатель
|
||||
не гадал, почему так.
|
||||
|
||||
### (3) Low «в» — проверка пинов сама себя отключает
|
||||
|
||||
`test/validate-workflow.test.mjs:216`:
|
||||
|
||||
```js
|
||||
if (!/pytest-homeassistant-custom-component|tests_backend\/requirements\.txt/.test(workflow)) continue;
|
||||
```
|
||||
|
||||
Если workflow не содержит ни имени пакета, ни пути к файлу пинов, проверка
|
||||
пропускает файл молча. То есть возврат к `pip install pytest voluptuous …` без
|
||||
версий — ровно то, от чего защищались в #392 — гейт не заметит: исчезнут обе
|
||||
зацепки разом.
|
||||
|
||||
**Контракт**: отсутствие зацепок — отказ, а не пропуск. Если workflow ставит
|
||||
зависимости бэкенда, он обязан делать это из файла пинов; если не ставит вовсе
|
||||
— это тоже утверждение, которое надо доказать, а не предположить.
|
||||
|
||||
## Скоуп / не-скоуп
|
||||
|
||||
**В скоупе**: `tests_backend/requirements.txt`, `pyproject.toml` (`[tool.ruff]
|
||||
include`) и/или шаг линта в `.github/workflows/validate.yml`,
|
||||
`test/validate-workflow.test.mjs`, при выборе исхода (2) — разбор ruff-долга в
|
||||
`scripts/*.py` и `tests_backend/**/*.py`.
|
||||
|
||||
**Не в скоупе**: сам факт сужения линта из #42 как решение (пересматривается
|
||||
только его видимость в конфиге); версии `homeassistant` и
|
||||
`pytest-homeassistant-custom-component` (закреплены #392, менять только вместе
|
||||
с полным прогоном); гейт типизации (#42) и гвард `sys.modules` (#398).
|
||||
|
||||
## UX
|
||||
|
||||
Не применимо.
|
||||
|
||||
## Модель данных и миграция
|
||||
|
||||
Не применимо.
|
||||
|
||||
## i18n
|
||||
|
||||
Новых строк нет.
|
||||
|
||||
## Критерии приёмки
|
||||
|
||||
- **AC1**. `home-assistant-frontend` в `tests_backend/requirements.txt` равен
|
||||
версии из констрейнтов закреплённого `homeassistant`. Доказательство: тест,
|
||||
сверяющий пин с зафиксированной в репозитории копией ожидаемой версии (без
|
||||
обращения в сеть на прогоне), плюс комментарий в файле с источником.
|
||||
- **AC2**. Скоуп линта в workflow и `include` в `pyproject.toml` совпадают.
|
||||
Доказательство: контрактный тест, читающий оба файла и сравнивающий списки
|
||||
деревьев; расхождение краснеет.
|
||||
- **AC3**. Выбранный исход (1) или (2) записан комментарием рядом с `include`
|
||||
с причиной. Доказательство: тест на присутствие обоснования не требуется —
|
||||
проверяется ревьюером; но при исходе (2) `ruff check` по полному `include`
|
||||
зелёный в CI.
|
||||
- **AC4**. `test/validate-workflow.test.mjs` отказывает, когда в workflow нет
|
||||
ни имени пакета, ни пути к файлу пинов. Доказательство: тест на синтетическом
|
||||
workflow без обеих зацепок → красный.
|
||||
- **AC5**. Проверка при этом не ломается на workflow, которые бэкенд-зависимости
|
||||
не ставят вовсе (если такие есть в репозитории): для них условие формулируется
|
||||
явно, а не через отсутствие подстроки. Доказательство: перечисление файлов,
|
||||
которые проверка обходит, зафиксировано в самом тесте.
|
||||
- **AC6**. Существующие гейты не ослаблены: `npm test`, `ruff` по текущему
|
||||
CI-скоупу и `mypy` strict остаются зелёными.
|
||||
|
||||
## План автотестов
|
||||
|
||||
**Unit** (`test/validate-workflow.test.mjs`, `test/backend-pins.test.mjs`):
|
||||
|
||||
1. Пин фронтенда совпадает с ожидаемой версией закреплённого HA (AC1).
|
||||
2. Списки деревьев в `include` и в шаге линта совпадают (AC2).
|
||||
3. Синтетический workflow без обеих зацепок → проверка пинов краснеет (AC4).
|
||||
4. Явный список обходимых файлов зафиксирован; добавление нового файла в
|
||||
workflows без обновления списка краснеет (AC5).
|
||||
|
||||
**Мутанты** (`scripts/mutation-gate.mjs`):
|
||||
|
||||
- `backend-pins-check-opts-out`: убрать из workflow путь к файлу пинов →
|
||||
`validate-workflow.test.mjs` красный.
|
||||
- `lint-scope-drifts`: добавить дерево в `include`, не тронув workflow →
|
||||
контракт скоупа красный.
|
||||
|
||||
## Риски
|
||||
|
||||
- **Исход (2) вскроет долг, который придётся чинить в этой же задаче.**
|
||||
Смягчение: оценить объём до выбора; при неприемлемом объёме брать исход (1),
|
||||
а расширение выносить отдельным issue со своим бюджетом.
|
||||
- **Сверка версии фронтенда без сети.** Ожидаемая версия должна лежать в
|
||||
репозитории, иначе тест станет зависеть от доступности GitHub. Смягчение:
|
||||
хранить ожидание рядом с пином (комментарий + константа в тесте), обновлять
|
||||
вместе с подъёмом HA — это и есть «видно по SHA».
|
||||
- **Ложное чувство завершённости.** Задача чинит видимость, а не покрытие:
|
||||
после неё линт по-прежнему может проверять одно дерево. Смягчение: явная
|
||||
формулировка в комментарии и, при исходе (1), заведённый follow-up.
|
||||
|
||||
## Откат
|
||||
|
||||
Три независимые правки, каждая — одна строка плюс тест. Продуктовый код не
|
||||
затронут.
|
||||
|
||||
## Release-артефакты
|
||||
|
||||
Пользовательских изменений нет: `User-Visible: no`, changelog не трогается.
|
||||
`docs/ARCHITECTURE.md` — одна строка в разделе про гейты бэкенда, если выбран
|
||||
исход (2).
|
||||
Reference in New Issue
Block a user