fix(drift): F1+F2 — the sweep refuses false greens, and CI provisions the real floor (t39)

t38 found two holes in the drift gate itself.

F2 — the job could never run where it mattered. Home Assistant 2026.10.1
declares ``Requires-Python >=3.14.2`` (PyPI metadata), so the old guard let a
3.13 runner through and the ``homeassistant>=2026.10`` install then failed with
a resolver error. The job now provisions its own interpreter
(``actions/setup-python@v5`` with ``python-version: "3.14"``) and the guard runs
against the REAL floor, read from ``ha_api_drift.HOMEASSISTANT_PYTHON_FLOOR`` —
one source of truth, pinned by a test against the installed distribution's own
``Requires-Python`` metadata. Verified: on 3.13.16 the guard refuses with
"Home Assistant needs Python >= 3.14.2, but this job runs 3.13.16 / Fix: raise
actions/setup-python…"; on 3.14.8 it reports "interpreter ok"; and
``uvx --python 3.14 --with 'homeassistant>=2026.10'`` resolves 2026.10.1.

F1 — the standalone entry point was a false green. It printed counters and
exited 0 even when it had walked nothing, which is exactly how the manual
red-proof tool gets misused. The non-vacuity floors now live in
``ha_api_drift.FLOORS`` and are enforced in ONE place, ``Report.floor_violations()``,
used by the pytest wrapper and by ``__main__`` (so the constants cannot drift
apart; a test asserts the wrapper holds no floor-like constants of its own).
On top of that the sweep can no longer lie in either direction:

* an empty or mis-targeted directory reports ``FLOOR:`` lines and
  ``drift sweep FAILED`` and exits 1 (a non-existent path is a ``bad-path``
  finding, not a traceback and not a silent zero-module sweep);
* with no homeassistant installed it returns a single ``no-homeassistant``
  finding with zero counters — never a wall of phantom "broken-import"s, never
  a green exit.

Verified in the CI-equivalent environment (Python 3.14.8 + homeassistant
2026.10.1): HEAD exits 0 with "no Home-Assistant API drift (13 modules, 60
imports, 35 call arguments, 7 awaited HA calls)"; the empty directory exits 1
with 4 FLOOR lines and no findings; a missing path exits 1 with the ``bad-path``
finding; and the full pre-fix tree (``git archive 5ede4fe``) exits 1 with
exactly the three t36 drifts (media_player.py:965 BrowseMediaSource,
diagnostics.py:93 homeassistant.util.diagnostic, config_flow.py:425
missing-await) and 0 floor violations.

The fast ``test`` job and ``release.yml`` are untouched; the workflow is
unchanged in shape except the new provisioning + guard steps, and parses as
valid YAML.
This commit is contained in:
James Mackay committed 2026-10-10 21:29:39 +01:00
1 parent 824d6192a1
commit fd62d850b7
3 files changed
+222 -18

No files matched your search

+32 -9
View File
@@ -63,17 +63,40 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Check the runner Python can host Home Assistant
- name: Provision the interpreter Home Assistant needs
uses: actions/setup-python@v5
with:
# Home Assistant 2026.10.1 declares Requires-Python >= 3.14.2 (PyPI
# metadata; tests/ha_api_drift.py holds our copy of that number and the
# drift suite pins it against the installed distribution), so the
# runner's own python3 cannot install the version this gate must run
# against. This resolves the newest 3.14.x patch; the guard below
# refuses anything still short of the floor rather than letting the
# install fail halfway through.
python-version: "3.14"
- name: Check the interpreter satisfies Home Assistant's floor
run: |
set -euo pipefail
# Home Assistant 2026.x requires Python >= 3.13; a newer runner just
# works. Fail loudly with the fix instead of a confusing resolver
# error when the image is older.
if ! python3 -c 'import sys; sys.exit(0 if sys.version_info >= (3, 13) else 1)'; then
echo "Home Assistant needs Python >= 3.13 but this runner has $(python3 -V)."
echo "Add 'actions/setup-python@v5' with python-version '3.13' to this job."
exit 1
fi
# The floor comes from tests/ha_api_drift.py — one source of truth for
# this guard and the metadata pin in tests/test_ha_api_drift.py.
python3 - <<'PY'
import sys
from pathlib import Path
sys.path.insert(0, str(Path("tests").resolve()))
from ha_api_drift import HOMEASSISTANT_PYTHON_FLOOR as FLOOR # noqa: E402
needed = ".".join(str(part) for part in FLOOR)
if sys.version_info < FLOOR:
print(
f"Home Assistant needs Python >= {needed}, but this job runs "
f"{sys.version.split()[0]}."
)
print(f"Fix: raise actions/setup-python in this job to a >= {needed} build.")
raise SystemExit(1)
print(f"interpreter ok: Python {sys.version.split()[0]} satisfies >= {needed}")
PY
- name: Install Home Assistant + test tooling
run: |