Repository navigation
A cross-reference role can name a class the tree no longer has #2539
Description
What was observed
emrg/server/scheduler.py documents the durable task-run store. The docstring of read_task_run_records says the daemon reports the instance's record through Daemon._evolution_count, written as a Sphinx meth cross-reference.
There is no Daemon class in this tree. The class is EmrgServer (emrg/server/daemon.py), and _evolution_count is a method on it (daemon.py:1514). The same docstring's other pointer, at TaskHandler._load_task_runs, is correct — so the shape was written right and one name drifted when the class was renamed.
Nothing reports this. The path opens, the module parses, the name it cites exists as a different thing, and a reader who follows the pointer finds no such class. grep for the cited name returns the citing sentence and nothing else.
What should exist instead
A guard over Sphinx cross-reference roles — func, class, meth, data — asserting that the name a role cites is one this tree binds: a def, a class, an assignment target or an import anywhere under emrg/, scripts/ or tests/, or a builtin.
A dotted name is decided on its head, not its tail, because static resolution cannot follow os.path.expandvars: the head must be imported in the citing file, bound somewhere in the tree, or a builtin. That is the decidable half of the claim — do not cite through a module the file never opened.
The attr role stays outside the scan on purpose: it names a member of an object rather than a binding a module holds, so the tree's attr references are ctypes structure _fields_ entries, and a rule that read them as module bindings would report three sites and be wrong three times.
What the guard does not measure
Whether the cited name is the right one. A reference to a real function that has stopped doing what the sentence says is invisible to it. It also does not read a bare backticked name in prose — which is how the defect that motivated the work was spelled. A backtick in prose is not a declaration, and the tree holds hundreds naming files, config keys and protocol frames rather than code; a rule over them would report prose and be deleted.
Where it lives
tests/test_docstring_cross_references_resolve.py, a third sibling in a family that already holds two: tests/test_check_citation_resolves.py (a tests/test_x.py::node citation must collect) and tests/test_docstring_title_citations_resolve.py (a "see Title in the module docstring" citation must land on a heading the module carries). Each has its own resolution rule, so each has its own file.
Measured on master @ d3c8807c: 251 role references across the three directories, 1 resolved to nothing — the site above, fixed in the same change.
Activity
argszero commentedon Oct 23, 2026
Handled by #2540
emrg: a cross-reference names a symbol the tree has (#2539) (#2540)