diff --git a/tests/test_orphan_sweep.py b/tests/test_orphan_sweep.py index f6a5c39..76d98ca 100644 --- a/tests/test_orphan_sweep.py +++ b/tests/test_orphan_sweep.py @@ -6,10 +6,10 @@ guard makes that sweep reproducible: it locks the current set of unreferenced pu definitions, so a newly stranded function fails the build instead of accumulating silently, and deleting a known orphan requires deleting its entry here. -The sweep is intentionally conservative. It only considers module-level public -definitions, and it honours the dynamic-wiring exceptions the skill calls out: -framework route registration, string-based entrypoint references, and use from -`tests/` or `tools/`. +The sweep is intentionally conservative. It considers module-level public +definitions and public methods on public classes, and honours the dynamic-wiring +exceptions the skill calls out: framework route registration, string-based +entrypoint references, and use from `tests/` or `tools/`. """ from __future__ import annotations @@ -36,6 +36,62 @@ KNOWN_ORPHANS: dict[str, str] = { "Uncertain - follow-up: confirm whether the benchmarking entrypoint is still " "intended before removing." ), + "DocumentService.is_document_type_referenced": ( + "Read helper currently bypassed by callers in favor of direct delete guards. " + "Retained for now to preserve service API stability during cleanup." + ), + "DocumentService.is_tag_referenced": ( + "Read helper currently bypassed by callers in favor of direct delete guards. " + "Retained for now to preserve service API stability during cleanup." + ), + "DocumentService.read_document": ( + "Public CRUD read method currently unused by runtime routes, but retained as " + "part of the service API shape for downstream call sites." + ), + "DocumentService.read_document_type": ( + "Public CRUD read method currently unused by runtime routes, but retained as " + "part of the service API shape for downstream call sites." + ), + "DocumentService.read_tag": ( + "Public CRUD read method currently unused by runtime routes, but retained as " + "part of the service API shape for downstream call sites." + ), + "JSONBCompat.load_dialect_impl": ( + "SQLAlchemy type hook is invoked by ORM internals via subclass protocol rather " + "than direct in-repo references; keep this implementation method." + ), + "JobService.delete_job": ( + "Legacy delete method retained as a compatibility shim while guarded deletion " + "flows migrate fully to delete_job_with_guardrails." + ), + "JobService.update_job": ( + "Legacy update method retained for compatibility while route and workflow " + "callers continue converging on narrower state-transition APIs." + ), + "PeopleService.is_person_role_referenced": ( + "Reference-check helper currently not called by route workflows, but kept with " + "the service surface while role-management cleanup remains in progress." + ), + "PeopleService.read_person": ( + "Public CRUD read method currently unused by runtime routes, but retained as " + "part of the service API shape for downstream call sites." + ), + "PeopleService.read_person_role": ( + "Public CRUD read method currently unused by runtime routes, but retained as " + "part of the service API shape for downstream call sites." + ), + "Settings.normalize_provider_models": ( + "Pydantic model-level validator is executed by framework hooks using decorator " + "registration and therefore has no direct symbolic call site." + ), + "Settings.validate_provider_models_input": ( + "Pydantic field validator is executed by framework hooks using decorator " + "registration and therefore has no direct symbolic call site." + ), + "SourceService.read_job_source_for_job": ( + "Public read helper currently unused by runtime flows, but retained as part of " + "the SourceService API pending endpoint consolidation." + ), "dispose_all_engines": ( "Engine lifecycle helper in db/engine.py. Uncertain - follow-up: operational " "teardown utility with no runtime or test caller." @@ -61,7 +117,7 @@ def _is_registered_with_framework(node: ast.FunctionDef | ast.AsyncFunctionDef | def _public_definitions() -> dict[str, str]: - """Public module-level definitions, mapped to `path:line`.""" + """Public definitions mapped to `path:line` (module-level + class methods).""" definitions: dict[str, str] = {} for path in _source_files(): tree = ast.parse(path.read_text(encoding="utf-8")) @@ -71,6 +127,15 @@ def _public_definitions() -> dict[str, str]: if node.name.startswith("_") or _is_registered_with_framework(node): continue definitions[node.name] = f"{path.relative_to(PROJECT_ROOT).as_posix()}:{node.lineno}" + if isinstance(node, ast.ClassDef): + for method in node.body: + if not isinstance(method, ast.FunctionDef | ast.AsyncFunctionDef): + continue + if method.name.startswith("_"): + continue + definitions[f"{node.name}.{method.name}"] = ( + f"{path.relative_to(PROJECT_ROOT).as_posix()}:{method.lineno}" + ) return definitions @@ -107,14 +172,19 @@ def _orphans() -> dict[str, str]: for name, location in definitions.items() # A definition is referenced if its name is used directly, or appears inside a # string such as "transcription.__main__:create_cli_app". - if name not in names and name not in literal_blob + if ( + name not in names + and name not in literal_blob + and name.split(".")[-1] not in names + and name.split(".")[-1] not in literal_blob + ) } def test_public_definitions_are_discovered(): """Guard the guard: the sweep is meaningless if nothing is scanned.""" definitions = _public_definitions() - assert len(definitions) >= 260 + assert len(definitions) >= 420 assert "create_app" in definitions