Implementation Plan: Split the orchestrator-api commands god-module
Branch: claude/funny-cannon-2kiwuz | Date: 2026-10-04 | Spec: spec.md Input: Mission specification from kitty-specs/orchestrator-api-commands-degod-01M43CFW/spec.md
Summary
src/specify_cli/orchestrator_api/commands.py (4,191 LOC) holds all 21 subcommands of the external orchestrator-api contract. We split it into one shared-helper module and five concern modules. commands.py stays the façade: it keeps the Typer app, the JSON error group, contract-version, and the two #5532 readers (mission-state, list-ready). It registers every other verb explicitly, in today's order, from a command table. Function bodies move verbatim.
Technical Context
Language/Version: Python 3.11 Primary Dependencies: typer / click (vendored-click exception shims stay in the façade) Storage: N/A Testing: help-snapshot byte diff (before/after) plus the orchestrator-api blast-radius suite (baseline 1197 passed, 2 skipped, 2 xfailed) Target Platform: CLI Project Type: single Performance Goals: N/A (import cost unchanged: the façade imports every concern module eagerly, as today) Constraints: byte-identical contract; no new suppressions; complexity ≤15 is unchanged because bodies do not change Scale/Scope: 1 package, ~4,200 LOC moved
Charter Check
- Single canonical authority: one
app, one shared-helper module. No helper is duplicated across concern modules. - Architectural alignment:
orchestrator_apistays insidespecify_cli. No new cross-package imports. - ATDD / red-first: this is a behaviour-preserving refactor with no defect to reproduce. The guard is a characterization test that pins the help snapshot and verb order. It is green before and after.
- Architectural gates that scan
commands.pyby path are re-pointed to the module that now holds the scanned code, in the same commit (gate-companion fold).
Project Structure
Source Code (repository root)
src/specify_cli/orchestrator_api/
├── commands.py # façade: app, _JSONErrorGroup, contract-version, mission-state, list-ready, command table
├── _common.py # envelope/failure helpers, policy parsing, repo-root + mission/WP resolution
├── wp_lifecycle.py # resolve-workspace, start-implementation, start-review, transition, append-history
├── consolidation.py # accept-mission, consolidate-mission, merge preflight/execute/cleanup helpers
├── design_phase.py # specify, plan, tasks, check-prerequisites, record-analysis
├── decision_verbs.py # open/resolve/defer/cancel/answer-decision
└── design_status.py # design-status
Structure Decision: The concern modules define plain functions with their typer Option signatures. They do not import app. The façade owns registration, so the command order lives in one place.
Seam rule: concern modules call the shared patch seams (_get_main_repo_root, _resolve_mission_dir_or_fail, _mission_identity_payload) through the _common module attribute, so a test that patches _common.<name> reaches every caller. Tests that patched commands.<name> move their patch target to the module that now owns the lookup.
Implementation Concern Map
IC-01 — Shared helpers
- Purpose: Give the envelope, failure, policy and resolution helpers one home.
- Relevant requirements: FR-003
- Affected surfaces:
orchestrator_api/_common.py,commands.py - Sequencing/depends-on: none
- Risks: Silent patch misses if a concern module binds a seam with
from ._common import X. Mitigated by the seam rule.
IC-02 — Concern modules + façade registration
- Purpose: Move each verb group verbatim and register it from the façade in today's order.
- Relevant requirements: FR-001, FR-002, FR-004
- Affected surfaces: the five concern modules,
commands.py - Sequencing/depends-on: IC-01
- Risks: Command reordering in
--help(the snapshot catches it). A helper used by only one concern must live with that concern, not in_common.
IC-03 — Gates and tests follow the code
- Purpose: Re-point source-scanning gates and patch targets so they stay non-vacuous.
- Relevant requirements: C-003
- Affected surfaces:
tests/architectural/test_destructive_op_routing.py,tests/specify_cli/regression/test_issue_1615_1616_1617_1618.py,tests/coordination/test_commit_outcome_consumer_pin.py,tests/runtime/test_bridge_engine.py, the two_COMMANDS_PYsource scans undertests/specify_cli/orchestrator_api/, and everypatch("…commands._…")target - Sequencing/depends-on: IC-02
- Risks: A scan that now reads a file lacking the pattern passes vacuously. Mitigated by re-pointing each scan to the file that holds the pattern; the scans assert presence.