Implementation Plan: Decompose agent/mission.py god-module (remainder) (#2056)
Branch: prog/2056-mission | Date: 2026-06-24 | Spec: spec.md Input: Mission specification from kitty-specs/decompose-mission-god-module-01KVXHF8/spec.md
Summary
src/specify_cli/cli/commands/agent/mission.py is a 4125 LOC / 62-def god-module that hosts the 8-subcommand agent mission CLI. Three command functions are mega-functions (finalize_tasks 1227 LOC, setup_plan 507, create_mission 281). The planning-commit pipeline was already extracted to coordination/commit_router.py (mission 01KVMBD6). This mission decomposes the REMAINDER into 4 cohesive, independently-testable seams behind a thin command-registration shim, internally decomposes the 3 mega-functions to <=15 complexity, re-exports every test-patched symbol (~100) so existing patch targets keep resolving with zero churn, and relocates the LIVE planning-commit residue (_planning_commit_worktree/_resolve_planning_placement/_stage_finalize_artifacts_in_coord_worktree) into commit_router.py with tasks.py repointed. Pure behavior-preserving refactor (profile randy-reducer); CLI surface stays byte-for-byte. The technical approach is dictated by the resolved research (seam map in research.md, topology in data-model.md): extract Seam D first (stable resolution surface), then C, A, B-per-family, with each mega-function decomposed in the WP that moves its command.
Technical Context
Language/Version: Python 3.11+ Primary Dependencies: typer (CLI), rich (console), mission_runtime (CommitTarget, MissionArtifactKind, topology), coordination.commit_router (commit_for_mission pipeline), status package, core., sync.. Storage: N/A (no persistent data structures change; this is a code-topology refactor). Testing: pytest (≈50 existing mission-touching test files) + new focused unit tests per seam and per phase helper + a NEW golden CLI characterization test (typer.testing.CliRunner). Target Platform: Linux/macOS dev + CI. Project Type: single (Python package src/specify_cli). Performance Goals: N/A (no hot path changed). Import-time must not regress (keep lazy imports for commit_for_mission / CoordinationWorkspace). Constraints: maxCC <=15 (ruff C901 / Sonar S3776); ≥90% new-code coverage; ruff + mypy --strict clean; no new suppressions; CLI surface byte-for-byte frozen; no import cycles (seams → lower layers only; commit_router never imports mission/seams). Scale/Scope: 1 god-module (4125 LOC, 62 defs) → 1 thin shim + ~9 seam modules + relocation into 1 existing module (commit_router.py) + 1 import repoint (tasks.py). ~100 re-exports preserved.
Charter Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
(alias surface for --mission) is preserved verbatim, not renamed (renaming would break the flag). Canonical "Mission" used in all new prose.
canonical coordination/commit_router.py (C-002), not a hand-rolled new module. Seams import canonical lower layers.
branch flow); no direct origin/main push.
- Terminology Canon — PASS. No new
feature*aliases introduced; existing param namefeature - Canonical sources, never improvise — PASS. The relocated planning-commit residue lands in the
- No new suppressions — PASS by design (NFR-004 / C-004).
- Complexity ceiling 15 — PASS by design (NFR-001 drives the mega-function decomposition).
- Every new branch/helper needs tests in the same PR — PASS by design (FR-004 / FR-005 / NFR-002).
- Git workflow — PASS. Planning on
prog/2056-mission; merge targetmain(non-protected program
No charter violations. Complexity Tracking table below is therefore empty.
Project Structure
Documentation (this mission)
kitty-specs/decompose-mission-god-module-01KVXHF8/
├── plan.md # This file
├── spec.md # Mission spec (FR-001..FR-007, NFRs, constraints)
├── research.md # Seam map (62-def inventory, mega-functions, coupling)
├── data-model.md # Target topology + invariants INV-1..INV-9
├── quickstart.md # How to verify the refactor locally
├── contracts/
│ └── cli-surface-contract.md # Frozen 8-command × all-flags contract
├── checklists/
│ └── requirements.md
├── tasks.md # Work-package manifest (linear chain)
└── tasks/ # WP prompt files
Source Code (repository root)
src/specify_cli/cli/commands/agent/
mission.py # THIN SHIM: app=Typer(name="mission"), registers 8 commands,
# re-exports ~100 previously-importable / test-patched symbols.
# #2056 pointer comment. No business logic. (Final WP owns this file.)
mission_feature_resolution.py # Seam D — _find_feature_directory & friends, _safe_load_meta,
# _read_feature_meta, _build_setup_plan_detection_error,
# _list_feature_spec_candidates, _sole_mission_slug_or_none,
# _resolve_mission_dir_name_primary_anchored, _primary_anchored_feature_dir
mission_parsing.py # Seam C — tasks.md/spec.md parsers, owned-files validation,
# JSON emit shims (_emit_json/_with_cli_version/_with_mission_aliases/
# _emit_console_or_json_error/_utc_now_iso)
mission_record_analysis.py # Seam A — record_analysis + _enforce_analysis_report_write_preflight +
# _resolve_record_analysis_placement_ref
mission_branch_context.py # Seam B — branch_context + _inject_branch_contract + branch helpers
mission_create.py # Seam B — create_mission (internally decomposed to phase helpers)
mission_check_prerequisites.py # Seam B — check_prerequisites + emit helpers
mission_setup_plan.py # Seam B — setup_plan (internally decomposed) + _commit_to_branch +
# CommitToBranchResult + _kind_for_artifact + _artifact_* helpers
mission_finalize.py # Seam B — finalize_tasks (internally decomposed) + finalize helpers
# (_collect_finalize_artifacts etc.)
mission_accept_merge.py # Seam B — accept_feature, merge_feature, worktree helpers
src/specify_cli/coordination/
commit_router.py # EXISTING — RECEIVES relocated _planning_commit_worktree /
# _resolve_planning_placement / _stage_finalize_artifacts_in_coord_worktree;
# reconciles _stage_finalize_artifacts_in_coord_worktree against existing
# _stage_artifacts_in_coord_worktree. (commit_router-relocation WP owns this.)
src/specify_cli/cli/commands/agent/
tasks.py # EXISTING — single function-local import line repointed from
# mission to commit_router. (commit_router-relocation WP touches this
# one line; out-of-map, documented.)
Structure Decision: Sibling-module layout (not a mission/ package). mission.py stays the import anchor specify_cli.cli.commands.agent.mission and becomes a thin shim re-exporting from sibling mission_*.py modules. This preserves the import path and every mission.<name> patch target with the smallest diff (no __init__.py redirection, no package-relative import rewrites). Each seam is one module with one matching test module under tests/specify_cli/cli/commands/agent/. The planning-commit residue relocates into the canonical coordination/commit_router.py; tasks.py's import line is repointed there.
Complexity Tracking
No charter violations — table intentionally empty.
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| — | — | — |
Implementation Concern Map
> Implementation concerns are NOT work packages. /spec-kitty.tasks translates these into a > STRICTLY-LINEAR WP chain (WP02 deps WP01, …). One concern may fold the relevant mega-function > decomposition into its seam WP.
IC-01 — Frozen CLI contract & golden characterization harness
envelopes for success/error) as an executable golden test BEFORE any extraction, so every later WP can prove byte-for-byte preservation.
reads contracts/cli-surface-contract.md. Extends tests/integration/test_json_envelope_strict.py.
snapshot directly from CliRunner --help output for app and each subcommand, not from memory.
- Purpose: Capture the byte-for-byte
agent missionCLI surface (8 commands × all flags + JSON - Relevant requirements: FR-001, C-001, C-005, INV-1, INV-2.
- Affected surfaces: NEW
tests/specify_cli/cli/commands/agent/test_mission_cli_golden_contract.py; - Sequencing/depends-on: none (must run first).
- Risks: Missing a flag/default in the golden snapshot would let a later regression slip — derive the
IC-02 — Feature-dir resolution seam (Seam D)
Seams A/B/C import a stable surface rather than each other.
edits in mission.py (shim re-export) deferred to the final WP.
re-exported at mission.<name> (INV-4).
- Purpose: Extract the shared, most-patched (
_find_feature_directory39×) resolution surface FIRST so - Relevant requirements: FR-003, FR-004, FR-006, NFR-001, NFR-005, INV-3, INV-8.
- Affected surfaces: NEW
mission_feature_resolution.py+ test; small documented out-of-map import - Sequencing/depends-on: IC-01.
- Risks:
_build_setup_plan_detection_erroris also imported bylifecycle.py— must remain
IC-03 — Parsing & validation seam (Seam C)
shims; give the pure parsers DIRECT unit tests (current gap — only indirect coverage via finalize).
_parse_requirement_refs_from_tasks_md — must remain resolvable (via shim re-export).
envelope contract — extraction must not alter envelope keys (INV-2).
- Purpose: Extract the (mostly pure) tasks.md/spec.md parsers, owned-files validation, and JSON emit
- Relevant requirements: FR-003, FR-004, FR-006, NFR-001, NFR-002, INV-3.
- Affected surfaces: NEW
mission_parsing.py+ test.tasks.pyimports - Sequencing/depends-on: IC-02.
- Risks: JSON emit shims (
_emit_json/_with_cli_version/_with_mission_aliases) are central to the
IC-04 — Record-analysis seam (Seam A)
commit_router.commit_for_mission (lazy) + analysis_report.write_analysis_report.
- Purpose: Extract
record_analysis+ its 2 dedicated helpers — the lowest-risk command slice. - Relevant requirements: FR-003, FR-004, FR-006, NFR-001, INV-1, INV-3.
- Affected surfaces: NEW
mission_record_analysis.py+ test. Imports Seam C/D surfaces + - Sequencing/depends-on: IC-03.
- Risks: low — 2 dedicated test files already exist; extend, don't replace.
IC-05 — Lifecycle command families (Seam B) + mega-function decomposition
decompose the 3 mega-functions (finalize_tasks 1227, setup_plan 507, create_mission 281) into <=15-CC phase helpers as they move. Each phase helper gets a focused test.
mission_check_prerequisites.py, mission_setup_plan.py, mission_finalize.py, mission_accept_merge.py + tests.
extraction — pin with existing readonly test + assert the write phase is unreachable when validate_only. accept/merge are thin delegators (INV — A-3) and must not pull the full accept/merge graph at module top level.
- Purpose: Split the 7 lifecycle commands per family into per-command modules, and internally
- Relevant requirements: FR-003, FR-004, FR-005, FR-006, NFR-001, NFR-002, INV-1, INV-3, INV-6.
- Affected surfaces: NEW
mission_branch_context.py,mission_create.py, - Sequencing/depends-on: IC-04 (and each family WP depends linearly on the prior family WP).
- Risks:
finalize_tasks --validate-onlyzero-mutation invariant (INV-6) must survive phase
IC-06 — Planning-commit residue relocation (LIVE → commit_router) + tasks.py repoint
_stage_finalize_artifacts_in_coord_worktree into coordination/commit_router.py, reconcile against the existing near-duplicate _stage_artifacts_in_coord_worktree, and repoint tasks.py's import. These are LIVE on this base — relocate, never delete.
tasks.py single import line (out-of-map, documented).
references are gone and the move targets a clean boundary).
reconcile the staging helper rather than forking a duplicate (research O-2).
- Purpose: Relocate
_planning_commit_worktree,_resolve_planning_placement, - Relevant requirements: FR-007, C-002, NFR-005, INV-5, INV-7, INV-8.
- Affected surfaces:
coordination/commit_router.py(owned by this WP) + test; - Sequencing/depends-on: IC-05 (after the finalize seam moves, so the residue's last in-mission
- Risks: import cycle if
commit_routerimports back frommission/seams (INV-8) — keep one-way;
IC-07 — Shim finalization: #2056 pointer comment + ~100 re-export sweep + full gate sweep
re-export block (every test-patched name) + the #2056 pointer comment; run the full ruff/mypy/coverage gate sweep.
from the patch survey (research §5) and assert it with a re-export presence test.
- Purpose: Reduce
mission.pyto a thin shim:app+ 8 command registrations + the complete - Relevant requirements: FR-001, FR-002, FR-006, NFR-001..004, SC-1..SC-8, INV-1..INV-9.
- Affected surfaces:
mission.py(SOLE owner of this file across the whole mission). - Sequencing/depends-on: IC-06.
- Risks: a missed re-export breaks
@patch("...mission.<name>")somewhere — enumerate the full set