Research: Retire mission_read_path Backcompat Shim

Phase 0 output. This mission has no open [NEEDS CLARIFICATION] markers; the research below records the decisions and the investigation that de-risks the mechanical change.

D-01 — External backcompat consumers: safe to delete

API. A repo-wide grep shows zero src/ importers — every src/ reference is a docstring or comment, not an import. The last production importer (runtime/next/runtime_bridge.py) was re-pointed to the canonical seam by mission 01KVJPEQ. External direct-importers of an internal module path are unsupported.

reverse the ratchet bump, perpetuates the dead module the issue exists to remove. (b) defer the decision to implementation behind a gate — rejected: the HiC confirmed "safe to delete" up front.

  • Decision: Delete src/specify_cli/mission_read_path.py outright; do not keep a deprecated stub.
  • Rationale: specify_cli.mission_read_path is an internal module path, not a documented public
  • Alternatives considered: (a) keep module + add DeprecationWarning — rejected: does not

D-02 — Repoint target: alias the PRIVATE canonical worker

from specify_cli.missions._read_path_resolver import _resolve_mission_read_path as resolve_mission_read_path (and import StatusReadPathNotFound directly, since it is public).

a public symbol named resolve_mission_read_path. Mission 01KVJPEQ/WP01 privatized the worker to _resolve_mission_read_path and dropped it from __all__; the shim existed precisely to re-export it under the historical public name. Its signature _resolve_mission_read_path(repo_root, mission_slug, mid8, *, require_exists=False, …) matches the test's call sites exactly (resolve_mission_read_path(tmp_path, slug, mid8, require_exists=True)), so an import alias preserves the test bodies verbatim (NFR-003, C-002).

defined in _read_path_resolver.py; StatusReadPathNotFound is in __all__ and imports cleanly. The test only imports StatusReadPathNotFound (and resolve_mission_read_path), both reachable.

__all__ — rejected: re-grows a symbol surface 01KVJPEQ deliberately shrank, and the dead-symbol gate would then demand justified cross-module callers. (b) refactor the test to call a public entry point (resolve_handle_to_read_path) — rejected: changes what is under test; these cases specifically exercise the low-level coord-worktree-first selection of the 3-arg worker.

contains a string fixture (snippet = "… from specify_cli.missions._read_path_resolver import resolve_mission_read_path …") used to test a selection-bypass ratchet. It is injected source text, never executed as a real import, so it does not break. Left as-is.

  • Decision: Re-point the importer to
  • Rationale: The canonical module specify_cli.missions._read_path_resolver does not export
  • Error-contract names: StatusReadPathNotFound and STATUS_READ_PATH_NOT_FOUND_CODE are
  • Alternatives considered: (a) re-promote _resolve_mission_read_path to public / add to
  • Note on a non-importer reference: tests/architectural/test_single_mission_surface_resolver.py

D-03 — Atomicity of the dead-module gate and ratchet

change**.

_CATEGORY_4_BACKCOMPAT_SHIMS while the module still exists (now an un-allowlisted dead module), and test_ratchet_baselines.py fails if _baselines.yaml declares 8 while the live frozenset still contains 9 entries (C-001). The set {delete module, drop both allowlist entries, set baseline = 8} is internally consistent only when applied together.

working tree is consistent at review time; the gate only needs to be green at the end.

  • Decision: Land the module delete, allowlist removals, and baseline decrement in the **same
  • Rationale: test_no_dead_modules.py fails if "specify_cli.mission_read_path" is removed from
  • Alternatives considered: splitting across commits — acceptable within one WP as long as the

Investigation summary (importer census)

ReferenceKindAction
src/specify_cli/mission_read_path.pythe shim moduleDELETE
tests/specify_cli/cli/commands/test_coord_reader_fixes.py (7 sites)real from specify_cli.mission_read_path import …REPOINT to canonical, alias private worker
tests/integration/test_cli_status_mediation.pyimports from canonical _read_path_resolver alreadyNO CHANGE
tests/specify_cli/regression/test_issue_1615_1616_1617_1618.pyasserts the string resolve_mission_read_path in production sourceNO CHANGE
tests/architectural/test_single_mission_surface_resolver.py (line ~828)string fixture, not a real importNO CHANGE (tidy docstring at line ~100 only)
tests/architectural/_baselines.yamlratchet ledgercategory_4_backcompat_shims: 9 → 8 + justification
tests/architectural/test_no_dead_modules.py_CATEGORY_4_BACKCOMPAT_SHIMS allowlistDROP entry
tests/architectural/test_no_dead_symbols.py_CATEGORY_C_BACKCOMPAT_SHIM_REEXPORT allowlistDROP entry

Open risks

confirm it equals 8 before setting the baseline (C-001).

trip a "private member accessed" lint in some configs. Verify ruff/mypy stay green; if a rule fires, the canonical seam already intends this worker for in-repo use (the shim was its only public face), so a narrowly-scoped, justified suppression is acceptable per the charter's suppression policy — but confirm a real rule fires first rather than pre-emptively suppressing.

  • Off-by-one on the baseline. After dropping the allowlist entry, recount the live frozenset and
  • Private-import lint. Importing _resolve_mission_read_path (underscore-prefixed) in a test may