Skip to content

A cross-reference role can name a class the tree no longer has #2539

Closed

Description

@argszero

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

  1. argszero commented on Oct 23, 2026

    @argszero
    OwnerAuthor

    Handled by #2540

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions