Files
ersatztv/scripts/tests/test_guard_populations_derive_from_git.py
T
timothyandClaude Opus 5 febaad77d7 fix(887): the image build builds the SPA and does not test it
`docker/Dockerfile`'s web-build stage is gitless twice over — the build context is
`web/` + `design-system/` so there is no `.git`, and `node:22-bookworm-slim` ships no
git binary. Members of the SPA suite need one or the other, so running the suite there
required naming the ones that cannot run. That list was a population nothing derived:
#883 added a third member without updating the hand-written pair of `--exclude`s, and
because `Build & push image (amd64)` is `if: github.event_name != 'pull_request'` the
resulting red was unreachable on a PR. It landed on `main` and on the `v*` tag path
instead — every image build failed, `:latest` stopped being republished, and a release
cut would have failed at the image build.

Adding a third `--exclude` re-arms the trap, so the list is removed rather than
extended: the stage now lints, typechecks and BUILDS the SPA, and the suite runs once,
unfiltered, in `docker-build.yml`'s `test` job on a real checkout. `build` carries
`needs: [test, migrations, scan]`, so no image is published past a red suite.

`scripts/tests/test_image_build_delegates_the_spa_suite.py` holds both halves — the
negative one alone would be satisfied by deleting the `needs:` edge. Three populations,
all derived: tracked Dockerfiles and workflows from the git index, and which npm scripts
ARE the suite from `web/package.json` (so `test` is in and the Playwright `test:ui-e2e`
is out, with no exemption list). Publishing jobs come from the `docker/build-push-action`
step and the Dockerfile each builds from that step's own `file:` input, which is why
`ci-image.yml` is out of scope by derivation rather than by an entry that would outlive
its reason.

Four mutants witnessed red, each by the intended test: a filtered suite run put back
into the Dockerfile, the `needs:` edge deleted, and the gating run narrowed in both the
block and the single-line `run:` step forms.

Refs: #887
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019T79beF1Ufid3dXju4yqkF
2026-08-30 13:37:58 +02:00

617 lines
34 KiB
Python

"""No file population in this repo admits a file git does not track (ersatztv#806).
The regression for #778's third defect, hoisted to cover every guard that shares the mechanism
rather than being copied into each of them. `Path.rglob` enumerated `.husky/_/` — 17 husky shims
generated by `npm ci`, gitignored and untracked — so `test_remote_state_inventory.py` was RED on
every developer checkout and GREEN in CI, whose `script-tests` job checks out and pip-installs but
never runs `npm ci`. A guard that fails everywhere except where it runs trains its readers to ignore
it, and it did so on the artifact whose entire thesis is population correctness.
TWO PROOFS, because they fail differently and either alone leaves a hole.
* `test_the_primitive_REALLY_excludes_an_untracked_file` builds a throwaway git repo, commits one
file, leaves an identical sibling untracked, and runs the real derivation against it. It proves
the mechanism by EXECUTING it rather than by recognising its shape — no monkeypatching, no
stand-in for git. Nothing here is a claim about `git ls-files`; it is `git ls-files`.
* `test_no_derivation_admits_an_untracked_file` narrows the tracked set under each real derivation
and requires the dropped member to vanish from the population even though the file is still on
disk and still matches the scope. That is the property stated over the ACTUAL guards, so a
future refactor that quietly reintroduces a filesystem walk in any one of them fails here rather
than on somebody's laptop.
`DERIVATIONS` is the reason this file is not one near-copy per derivation: a guard that starts deriving a file
population registers here, and both proofs cover it for free. The register is hand-written and that
is a SCOPE decision, not a population one — per `testing.guard-derives-population-from-source`, a
scope mirroring an authoritative source needs its own equality check, and
`test_every_index_derived_module_is_registered` is it: it reads which modules import the shared
helper and demands each one appear below, so adding another derivation and forgetting this file is
red rather than silently uncovered.
"""
from __future__ import annotations
import ast
import glob
import os
import subprocess
import sys
from pathlib import Path
import pytest
from scripts.tests import test_ci_image_pin_population as image_pin
from scripts.tests import test_ci_status_context_uniqueness as ctx_unique
from scripts.tests import test_guard_inventory as guard_inventory
from scripts.tests import test_hook_fire_log as hook_fire
from scripts.tests import test_image_build_delegates_the_spa_suite as image_build
from scripts.tests import test_pr_changed_files as pr_changed
from scripts.tests import test_remote_state_inventory as remote_state
from scripts.tests import test_workflow_job_guards as job_guards
from scripts.tests import test_workflow_persist_credentials as persist_creds
from scripts.tests import tracked_files
REPO_ROOT = Path(__file__).resolve().parents[2]
TESTS_DIR = REPO_ROOT / "scripts" / "tests"
class _EmptyScan:
"""An exhausted ITERATOR that is also a context manager, standing in for `os.scandir`.
Both halves are load-bearing and each was missing in turn. `os.scandir` is used as
`with os.scandir(...) as it`, so a bare iterator broke the context-manager protocol; and
`os.walk` does `entry = next(scandir_it)` on the result, so an ITERABLE defining only
`__iter__` broke that. Either way the enumeration assertion still fired with the right message,
but the report also carried a TypeError about the harness — and a finding that arrives beside a
harness error invites doubting the finding rather than the code.
"""
def __iter__(self):
return self
def __next__(self):
raise StopIteration
def __enter__(self):
return self
def __exit__(self, *_exc):
return False
def close(self):
return None
def _as_relative_strings(members) -> set[str]:
"""Derivations return either repo-relative strings or absolute `Path`s; compare on one form.
Iterating into a set is also what DRAINS a derivation that returns a generator, which the
enumeration proof depends on — see its call site.
An unexpected member type is REPORTED rather than stringified. `str(m)` on anything at all meant
a derivation yielding, say, nested generators produced plausible-looking members and compared
equal to nothing, which is a population check passing over data it did not understand.
"""
out = set()
for m in members:
assert isinstance(m, str | Path), (
f"a derivation yielded {type(m).__name__} ({m!r}); populations here are repo-relative "
"strings or absolute Paths, and stringifying anything else would compare a plausible "
"value against a set that can never contain it."
)
out.add(str(Path(m).relative_to(REPO_ROOT)) if isinstance(m, Path) else str(m))
return out
# (label, callable, a floor below which the derivation has plainly broken). `test_hook_fire_log`
# floors the same population at the same number for its own coverage assertions; that is not a
# duplicate guard masking another, because the two protect different consumers from going
# vacuous — delete this one and THIS file's proofs iterate over nothing while reporting success.
DERIVATIONS = (
("test_guard_inventory.derived_guard_files", guard_inventory.derived_guard_files, 25),
("test_hook_fire_log.hook_scripts", hook_fire.hook_scripts, 10),
("test_image_build_delegates_the_spa_suite.dockerfiles", image_build.dockerfiles, 4),
("test_image_build_delegates_the_spa_suite.workflow_files", image_build.workflow_files, 5),
("test_ci_image_pin_population.workflow_files", image_pin.workflow_files, 5),
("test_ci_status_context_uniqueness.workflow_files", ctx_unique.workflow_files, 5),
("test_remote_state_inventory.derived_population", remote_state.derived_population, 40),
("test_pr_changed_files._workflow_files", pr_changed._workflow_files, 5),
("test_workflow_persist_credentials.workflow_files", persist_creds.workflow_files, 5),
("test_workflow_job_guards.workflow_files", job_guards.workflow_files, 5),
)
# The floor matters only to the anti-vacuity test; the two property tests take the pair, so an
# unused parameter cannot drift into looking like an assertion they make.
_IDS = [d[0] for d in DERIVATIONS]
_PAIRS = [(label, derive) for label, derive, _ in DERIVATIONS]
# Modules that import the shared derivation WITHOUT deriving a guard population. Kept here, beside
# DERIVATIONS, so adding one is an edit to this file that a reviewer sees.
POPULATION_EXEMPT = {
# Uses the index to assemble a HERMETIC tmp fixture copy; nothing in it asserts membership.
"test_ci_release_path_scan_job.py": "index-derived fixture copy, not a population",
}
_HELPER = "scripts.tests.tracked_files"
_PACKAGE = ["scripts", "tests"]
# ------------------------------------------------------------------------------------------------
# ANTI-VACUITY FIRST — every assertion below compares sets, and a derivation that collapsed to
# nothing would satisfy all of them while proving nothing.
# ------------------------------------------------------------------------------------------------
@pytest.mark.parametrize(("label", "derive", "floor"), DERIVATIONS, ids=_IDS)
def test_each_derivation_found_something(label, derive, floor):
members = _as_relative_strings(derive())
assert len(members) >= floor, (
f"{label} derived only {len(members)} members, below its floor of {floor} — the derivation "
"is broken, not the repo, and every set comparison built on it is vacuous."
)
# ------------------------------------------------------------------------------------------------
# PROOF 1 — the primitive, executed against a real git repo rather than described
# ------------------------------------------------------------------------------------------------
def test_the_primitive_REALLY_excludes_an_untracked_file(tmp_path, monkeypatch):
"""A tracked and an untracked file, identical in name shape and both on disk. Only one is in.
Run rather than reasoned about. This class of defect is produced by arguments about what a
traversal WOULD enumerate, and such arguments are locally convincing whether or not they are
right; only executing the traversal distinguishes the two.
"""
repo = tmp_path / "repo"
(repo / ".claude" / "hooks").mkdir(parents=True)
(repo / ".claude" / "hooks" / "committed.sh").write_text("#!/bin/sh\n")
(repo / ".claude" / "hooks" / "untracked.sh").write_text("#!/bin/sh\n")
def git(*args):
subprocess.run(["git", "-C", str(repo), *args], check=True, capture_output=True)
git("init", "-q")
git("config", "user.email", "guard@example.invalid")
git("config", "user.name", "guard")
git("add", ".claude/hooks/committed.sh")
git("commit", "-qm", "one tracked hook")
monkeypatch.setattr(tracked_files, "REPO_ROOT", repo)
found = tracked_files.tracked_children(".claude/hooks", ("*.sh",))
assert (repo / ".claude" / "hooks" / "untracked.sh").is_file(), (
"the untracked file must still be on disk, or this proves nothing about the index winning over the filesystem"
)
assert found == {".claude/hooks/committed.sh"}, (
f"the derivation returned {sorted(found)}. A filesystem walk returns both files here; only "
"the index distinguishes them, and that difference is the entire point of ersatztv#806."
)
def test_the_primitive_does_not_recurse_into_an_untracked_subdirectory(tmp_path, monkeypatch):
"""`.husky/_/` in miniature — the shape that made #778 red on every checkout.
Even a TRACKED nested file must stay out: `tracked_children` is direct-children-only by design,
and recursion is what dragged the shims in. Proving it with a tracked file makes the assertion
about the traversal rather than about the index, so the two properties cannot mask each other.
"""
repo = tmp_path / "repo"
(repo / ".husky" / "_").mkdir(parents=True)
(repo / ".husky" / "pre-commit").write_text("#!/bin/sh\n")
(repo / ".husky" / "_" / "husky.sh").write_text("#!/bin/sh\n")
def git(*args):
subprocess.run(["git", "-C", str(repo), *args], check=True, capture_output=True)
git("init", "-q")
git("config", "user.email", "guard@example.invalid")
git("config", "user.name", "guard")
git("add", "-A")
git("commit", "-qm", "husky plus a nested shim, both tracked")
monkeypatch.setattr(tracked_files, "REPO_ROOT", repo)
assert tracked_files.tracked_children(".husky", ("*",)) == {".husky/pre-commit"}, (
"a nested file entered a flat population — this is the `.husky/_/` shape, and it was red on "
"every developer checkout the last time it shipped"
)
def test_an_empty_index_FAILS_LOUDLY_rather_than_reporting_an_empty_population(tmp_path, monkeypatch):
"""The floor under every floor. A silent empty population is how a completeness guard reports
total coverage having examined nothing, which is the failure mode this repo has shipped twice
(#631, #751)."""
repo = tmp_path / "repo"
repo.mkdir()
subprocess.run(["git", "-C", str(repo), "init", "-q"], check=True, capture_output=True)
monkeypatch.setattr(tracked_files, "REPO_ROOT", repo)
with pytest.raises(AssertionError, match="reported nothing"):
tracked_files.tracked_children(".claude/hooks", ("*.sh",))
# ------------------------------------------------------------------------------------------------
# PROOF 2 — the property, over the real guards
# ------------------------------------------------------------------------------------------------
@pytest.mark.parametrize(("label", "derive"), _PAIRS, ids=_IDS)
def test_no_derivation_admits_an_untracked_file(label, derive):
"""Narrow the index, leave the disk alone, and require the member to disappear — for EVERY
member, one at a time.
EXHAUSTIVE RATHER THAN ONE VICTIM, and the difference is not thoroughness for its own sake.
`derived_guard_files` unions four sources; one victim is always drawn from whichever sorts
first, so a mutant putting only the third source back on a filesystem walk passes while the
proof reports on all four. A sample cannot see the source it did not draw from — this file
applying `testing.guard-derives-population-from-source` to itself.
The floors cannot substitute, and the numbers say why. Reproduce with:
PYTHONPATH=. python3 -c "from scripts.tests import test_guard_inventory as g; \
print(len(g.derived_guard_files()))"
61 members on 2026-08-22; suppressing a single contributor leaves 39 (`scripts/tests`), 48
(hooks) or 58 (husky), all far above the anti-vacuity floor of 25. The figures move whenever a
guard is added — they were 60/39/47/57 one commit earlier — so read them as an illustration of
the GAP, not as values to assert against. A floor tight enough to catch a lost source
would go red every time a guard is legitimately deleted, which is the wrong instrument.
Its own `monkeypatch` context, never the shared fixture instance: the function-scoped fixture is
the same object `conftest.py`'s autouse `isolate_hook_fire_log` patched, so calling `undo()` on
it here reverts `ETV_HOOK_FIRE_LOG_DIR` too. Since ersatztv#809 that reverts to the SESSION
directory `pytest_configure` set rather than to unset, so it no longer re-points anything at the
PRODUCTION log — but it still collapses THIS test's remaining per-test isolation into the shared
session dir, and reaching into another fixture's patch object to undo it is the habit worth not
having either way.
WHAT REMOVAL CANNOT SEE, so it is not read as more than it is: a source contributing ONLY
untracked members has nothing here to remove, and #778's defect was exactly that shape (an
`rglob` over `.husky/_/` adds 17 untracked members and removes none). That direction is
`test_no_derivation_ENUMERATES_the_filesystem` below; the two are complements, not duplicates.
"""
before = _as_relative_strings(derive())
assert before, f"{label} derived nothing; there is no victim to remove"
real = tracked_files._git_ls_files()
survivors = []
absent = []
with pytest.MonkeyPatch.context() as m:
for victim in sorted(before):
if not (REPO_ROOT / victim).is_file():
absent.append(victim)
continue
m.setattr(tracked_files, "_git_ls_files", lambda v=victim: [p for p in real if p != v])
if victim in _as_relative_strings(derive()):
survivors.append(victim)
assert not absent, (
f"{label} contains {absent}, which git tracks but are not on disk. The proof below asserts "
"that the INDEX decides while the file is still present; it cannot mean that for a member "
"that is missing, so this is reported rather than skipped."
)
assert not survivors, (
f"{label} still contains {survivors} after git stopped tracking them. Every one of those is "
"still on disk, so the derivation is reading the filesystem for that member and untracked "
"build output can redden it on a developer checkout while CI stays green (ersatztv#778)."
)
# The directory-listing APIs a Python file population is realistically written with. NOT every way a
# process can list a directory — `subprocess.run(["ls"])`, a module-level alias captured before the
# patch, and any C-level call all walk straight past this, all three verified by cold review. That
# bounds what the check below can claim, and the docstring says so rather than implying a sandbox.
# Reading a file stays allowed: `derived_guard_files` must read workflow bodies.
_ENUMERATORS = (
(Path, "glob"),
(Path, "rglob"),
(Path, "iterdir"),
(Path, "walk"),
(os, "listdir"),
(os, "walk"),
(os, "scandir"),
(glob, "glob"),
(glob, "iglob"),
)
@pytest.mark.parametrize(("label", "derive"), _PAIRS, ids=_IDS)
def test_no_derivation_ENUMERATES_the_filesystem(label, derive):
"""The ADD direction, without arranging any state: a derivation may READ files, but it may not
LIST a directory while it runs.
WHY THE ADD DIRECTION NEEDS ITS OWN TEST. Removing members from the index cannot see a source
that contributes ONLY untracked members: it adds and never takes away, so nothing of its is
available to remove. `.husky/_/` is that shape exactly — 17 shims `npm ci` writes, none ever in
the index — and it is the shape #778 shipped. Scope the claim precisely: a source appending
`(REPO_ROOT / ".husky" / "_").rglob("*")` leaves the removal proof green ON A MACHINE WHERE THAT
DIRECTORY IS ABSENT, which is the `script-tests` checkout. Where the shims exist, the appended
members are present and removal reddens too. The blind spot is an append-only source that yields
nothing HERE — which is exactly the CI shape, and exactly where a guard going quiet matters.
WHY THE PROPERTY IS "DOES NOT ENUMERATE" RATHER THAN "DOES NOT RETURN AN UNTRACKED FILE". Both
obvious formulations of the latter are machine-dependent, which is the very fault #806 exists to
remove:
* creating real probe files in the checkout and requiring they not enter. That needs a
`test_*.py` probe to reach the `scripts/tests` scope — a file pytest COLLECTS mid-session —
and its parametrised names collide across `-n auto` workers and concurrent sessions,
`finally` does not survive SIGKILL, and a concurrent `git add -A` can stage one. Defects in
the test, not in the thing tested.
* neutralising the shared primitive and requiring the derivation to go empty. That misses the
`.husky/_/` source on any machine where `.husky/_/` does not exist — which is every CI
checkout of `script-tests`, which never runs `npm ci`. Green where it runs, red only on a
laptop: the inverted asymmetry again, inside the proof written to abolish it.
Watching for the CALL needs no arranged state: an `rglob` issued while deriving is caught even
where the directory it walks is empty, because the evidence is the call rather than what it
returned. Reading is untouched, so a derivation may still parse the workflow bodies it scrapes
for referenced scripts.
WHAT IT DOES NOT COVER. The boundary is not "synchronous", which is what two earlier drafts
said and what measurement disproved — a thread that outlives the `derive()` call but finishes
while its result is being drained IS caught, as is a `__del__` firing during that drain. The
boundary is mechanical rather than temporal: a call to one of the SPIES is observed, wherever and
whenever it happens in this process before the assertion below. "While the patch is active"
under-claims it — the spy appends to a list that outlives the patch, so a reference captured
during the window and invoked after it still records. What decides observation is whether the
call goes through a spy, not when. The result is drained while the patch is installed, so a lazy
generator is reached.
NOT REACHED, because no spy was ever installed on that path. Enumeration HOISTED TO MODULE SCOPE
runs at import, before this test exists — the likeliest instance rather than a contrivance, since
`test_ci_image_pin_population.py` already precomputes `_DOC` that way — as does an `atexit` hook,
and a cached property warmed by the baseline call below. `from os import listdir` binds the real
function before any patch; `from os import walk` IS caught (it routes through the patched
`os.scandir`) and so is `from glob import glob` (through the patched `glob.iglob`). And anything
listing in ANOTHER PROCESS — a deliberate `subprocess.run(["ls"])`, or a forked child. This is a
regression guard against the shapes that arrive by accident, not a sandbox.
It cuts the other way too: any spy call at all reddens this test, so unrelated background thread
activity touching a patched name during the window would too. Nothing in this suite does that
today, and the report names the call, so a false red would be diagnosable rather than
mysterious.
It also cannot see a derivation that admits a HARDCODED path without listing anything
(`if (REPO_ROOT / "x.sh").exists(): add`) — listing is the commonest way to discover an untracked
member, not the only one. The removal proof above catches that shape, and catches memoisation,
which this one cannot: the baseline call below warms any cache outside the patch. The two are
complements.
"""
assert derive(), f"{label} derived nothing; this proof needs a baseline"
calls: list[str] = []
def _spy(what):
# RECORDS and returns empty rather than raising. Raising made the assertion "did an
# exception reach us", which a derivation defeats by catching it: a `try: ... except
# Exception: return set()` around an `rglob` enumerated the filesystem and this test passed,
# measured. The evidence is the CALL, so the call is what is asserted on.
def spy(*_args, **_kwargs):
calls.append(what)
# Iterator AND context manager: `os.scandir` is used as `with os.scandir(...) as it`,
# and a bare iterator made the failure report carry a test-induced TypeError about the
# context manager protocol alongside the real finding. The assertion fired correctly
# either way, but a report that blames the harness invites doubting the finding.
return _EmptyScan()
return spy
failure = None
with pytest.MonkeyPatch.context() as m:
for owner, name in _ENUMERATORS:
m.setattr(owner, name, _spy(f"{getattr(owner, '__name__', owner)}.{name}"), raising=False)
try:
# DRAINED inside the context, never `derive()` discarded. A derivation returning a lazy
# generator does its work when the caller drains it, so discarding the result moved the
# whole walk outside the patch: a generator yielding the index population and then
# appending `.husky/_` passed here and admitted 17 untracked shims on a checkout where
# that directory exists. `_as_relative_strings` is what drains it — it iterates into a
# set — so the call must stay here rather than being hoisted out or wrapped in something
# lazier.
_as_relative_strings(derive())
except BaseException as exc: # re-raised below, after the evidence has been judged
failure = exc
assert not calls, (
f"{label} enumerated the filesystem via {sorted(set(calls))} while deriving its population. "
"Directory listings report build output, generated shims and editor droppings, and differ "
"between the CI checkout and a developer's, so the member set stops being a property of the "
f"repo (ersatztv#778, #806)." + (f" It also raised: {failure!r}" if failure is not None else "")
)
if failure is not None:
raise failure
@pytest.mark.parametrize(("label", "derive"), _PAIRS, ids=_IDS)
def test_every_derived_member_is_tracked(label, derive):
"""The same property as an invariant over the real tree, which is the form that catches a
refactor going back to a filesystem walk without also touching this file."""
tracked = set(tracked_files._git_ls_files())
stray = sorted(m for m in _as_relative_strings(derive()) if m not in tracked)
assert not stray, f"{label} contains untracked path(s): {stray}"
# ------------------------------------------------------------------------------------------------
# THE SCOPE MIRROR ABOVE IS ITSELF CHECKED
# ------------------------------------------------------------------------------------------------
def _modules_importing_the_helper() -> set[str]:
"""Which `scripts/tests/test_*.py` import the shared derivation, by PARSING them.
`ast` rather than a substring scan, and the distinction is the point rather than tidiness. A
substring scan over source both EVADES and FALSELY FIRES here: `import scripts.tests.
tracked_files as tf` and `from scripts.tests import tracked_files as tf` escape a scan for
`"tracked_files import"`, while a comment merely citing `scripts/tests/tracked_files.py` matches
a scan for `"tracked_files."` and would redden a correct tree over prose.
`docs/decisions/records/testing/guard-derives-population-from-source.md` records why patching
such a predicate does not converge: it is not a parser. Python ships the parser, and a comment
is not a node at all.
WHAT THE PARSE DOES NOT REACH, stated rather than implied by the word "parsing": STATIC import
statements naming the helper. `importlib.import_module("scripts.tests.tracked_files")`, a
re-export through `scripts/tests/__init__.py`, and `from scripts.tests import *` are invisible,
verified by executing each. (`from scripts.tests.tracked_files import *` IS seen — it names the
module.) Those sit inside the same residual as a module that derives a population without the
helper at all — the residual named below — and no mechanical check closes it.
The FILE LIST is a filesystem walk on purpose, and it is not the defect this file forbids: it is
a superset check over what pytest itself collects, so an untracked stray `test_x.py` here makes
the guard MORE demanding, never blind. Using the index would let an unstaged new guard escape
registration, which is the wrong direction for a check about coverage.
"""
found = set()
for path in sorted(TESTS_DIR.glob("test_*.py")):
for node in ast.walk(ast.parse(path.read_text())):
# RESOLVE the name to an absolute module and compare exactly. Testing `base ==
# "tracked_files"` handled `from .tracked_files import x` but silently missed
# `from ..tests.tracked_files import x`, which resolves to the same helper — a false
# NEGATIVE, the direction that lets a module adopt the helper and escape registration.
# A suffix match instead over-accepts `from unrelated.package import tracked_files`,
# reddening a correct tree over a module this repo does not own. Resolution is the only
# form with neither failure: these files live in `scripts.tests`, so level 1 resolves to
# `scripts.tests` and level 2 to `scripts` — see the guard below for why there is no
# level 3.
if isinstance(node, ast.Import):
hit = any(a.name == _HELPER for a in node.names)
elif isinstance(node, ast.ImportFrom):
if node.level > len(_PACKAGE):
# Beyond the top-level package: Python raises ImportError for this, so it cannot
# be an import of the helper. Guarded explicitly because `_PACKAGE[:negative]`
# silently WRAPS — level 4 produced the same prefix as level 2 — which pinned an
# unimportable form as a valid detection. Valid levels here are exactly 1 and 2:
# 1 resolves to `scripts.tests`, 2 to `scripts`, and 3 or more is beyond the
# top-level package, which Python refuses.
continue
prefix = _PACKAGE[: len(_PACKAGE) - (node.level - 1)] if node.level else []
base_parts = prefix + ([node.module] if node.module else [])
base = ".".join(base_parts)
hit = base == _HELPER or any(f"{base}.{a.name}" == _HELPER for a in node.names)
else:
continue
if hit:
found.add(path.name)
break
return found
# (source, should the matcher see it). Every row is a form that has actually been mis-classified; the
# table is here so the next edit to `_modules_importing_the_helper` cannot re-open one silently.
# A false NEGATIVE lets a module adopt the helper and escape registration; a false POSITIVE reddens
# a correct tree over a module this repo does not own. Both directions are pinned.
_IMPORT_FORMS = (
("import scripts.tests.tracked_files as tf", True),
("from scripts.tests import tracked_files as tf", True),
("from scripts.tests.tracked_files import tracked_paths", True),
("from scripts.tests.tracked_files import *", True),
("from . import tracked_files", True),
("from .tracked_files import tracked_paths", True),
("from ..tests.tracked_files import tracked_paths", True),
# Beyond the top-level package from `scripts.tests`: Python raises ImportError, so there is
# nothing to detect. Pinned False so the negative-slicing wrap that once made it look
# detectable cannot come back.
("from ...scripts.tests.tracked_files import tracked_paths", False),
# Witnesses the negative-slice wrap specifically: without the level guard this one
# resolves through `scripts` and matches.
("from ....tests.tracked_files import tracked_paths", False),
("def f():\n from scripts.tests import tracked_files\n return tracked_files", True),
("from unrelated.package import tracked_files", False),
("from ..something import tracked_files", False),
("from .something import tracked_files", False),
("from ..other.tracked_files import x", False),
("# see scripts/tests/tracked_files.py for the rationale\nimport re", False),
# Pins PARSER VISIBILITY, not importability: executing this line really does import the helper.
# The row records that a static parse cannot see it — a known gap, pinned so it is not a
# surprise — and closing it would mean updating this row, which is the intended friction.
("import importlib\nimportlib.import_module('scripts.tests.tracked_files')", False),
)
@pytest.mark.parametrize(("source", "expected"), _IMPORT_FORMS, ids=[s.splitlines()[0][:48] for s, _ in _IMPORT_FORMS])
def test_the_import_matcher_classifies_every_reviewed_form(source, expected, monkeypatch):
class _Fake:
name = "test_probe.py"
def read_text(self):
return source
def __lt__(self, other):
return True
class _Dir:
def glob(self, _pattern):
return [_Fake()]
monkeypatch.setattr(sys.modules[__name__], "TESTS_DIR", _Dir())
seen = "test_probe.py" in _modules_importing_the_helper()
assert seen is expected, f"the import matcher {'missed' if expected else 'falsely matched'} this form:\n{source}"
def test_every_index_derived_module_is_registered():
"""`DERIVATIONS` is a hand-written mirror, so it gets an equality check rather than a promise.
Without this, adding another index-derived guard and forgetting to register it leaves that guard
unproven while this file reads as covering them all — a completeness claim standing behind a
hand-maintained list, which is the defect one altitude up (#773 Family C, and the shape that put
`MARKED_JOBS` in the record as a residual gap until #787 closed it).
THE SCOPE THIS CANNOT SEE, stated because a check described as complete stops being re-examined:
it detects modules that IMPORT the shared helper. A module deriving a file population some other
way — shelling out to `git ls-files` itself, or going back to `Path.rglob` — is invisible to it,
and no mechanical check can close that. `test_guard_inventory.py`'s own header argues the same
point about flagging filter-shaped guards by token, and #774 concluded there that the honest
answer is no. What is mechanised here is the case that actually recurs: someone adopts the
helper and forgets this file.
Registration is MODULE-level, not derivation-level, so a second population added inside an
already-registered module is covered only if it is registered too.
`POPULATION_EXEMPT` is the opt-out, and it lives HERE rather than as a marker comment in the
exempt file because the two directions are not symmetric: a false import-match only reddens,
while a false EXEMPTION is silent. A marker a file grants itself by containing a token is
trippable from that file's prose — this file's own error message names the token — so it would
be a one-line silent kill switch, the shape `test_ci_release_path_scan_job.py` argues against
for its recursion fence. Listing exemptions beside the registrations makes adding one a visible
edit here.
WHAT NO ASSERTION CAN DECIDE, dated so it is re-examined rather than assumed: whether an
exemption is still WARRANTED. A stale key and an unexplained one are both caught below, but an
exempt module that later grows a real derived population stays uncovered and silent. Reviewed
2026-08-22 — the single entry uses the helper only to assemble a tmp fixture copy and asserts
nothing about membership.
"""
exempt = {Path(__file__).name} | set(POPULATION_EXEMPT)
importers = _modules_importing_the_helper()
registered = {label.split(".", 1)[0] + ".py" for label, _, _ in DERIVATIONS}
# ANTI-VACUITY, and only that. A broken parse is caught loudly by `phantom` below — every
# registered module would go missing at once — so this is the cheaper, more specific signal, not
# the thing standing between a broken parse and a green run.
assert len(importers) >= len(DERIVATIONS), (
f"the import parse found only {sorted(importers)}, fewer modules than DERIVATIONS registers "
f"({sorted(registered)}) — the parse has broken."
)
stale = sorted(name for name in POPULATION_EXEMPT if name not in importers)
assert not stale, (
f"POPULATION_EXEMPT lists {stale}, which the parser no longer sees importing the shared "
"helper (renamed, "
"deleted, or the import removed). A stale exemption is worse than none: if the filename is "
"ever reused, the new module is exempt from birth without anyone deciding that."
)
thin = sorted(name for name, why in POPULATION_EXEMPT.items() if not str(why).strip())
assert not thin, f"POPULATION_EXEMPT entries with no stated reason: {thin}"
unregistered = sorted(importers - registered - exempt)
assert not unregistered, (
f"{unregistered} import the shared index derivation but are not in DERIVATIONS, so neither "
"proof in this file covers them. Add a named derivation function and register it, or add "
"the module to POPULATION_EXEMPT in this file if it imports the helper without deriving a "
"population."
)
phantom = sorted(registered - importers)
assert not phantom, (
f"DERIVATIONS registers {phantom}, which no longer import the shared helper. A row for "
"a derivation that is not there reads as coverage and is not."
)