Implementation Plan: Degod tasks.py — thin CLI over pure cores (Wave 1)
Branch: design/degod-tasks-2116 | Date: 2026-07-01 | Spec: spec.md Input: Feature specification from kitty-specs/tasks-py-degod-01KWF08S/spec.md
Summary
Decompose the agent tasks god-command (src/specify_cli/cli/commands/agent/tasks.py, ~3,617 LOC, 9 subcommands) into a thin CLI shell over pure decision/aggregation cores behind injected ports — a behavior-preserving (pure-parity) refactor closing #2116 under #2173. Approach (settled by two pre-plan squads + a 4-lens post-tasks squad): freeze the full CLI contract with a golden characterization harness first (freezing every move_task decision branch, not just the skip/refuse arms), co-design a stratified port set (2 program-reference ports — FsReader + a two-capability CoordCommitRouter [commit_status + commit_artifact, disjoint seams] — + 2 mission-local seams), extract three pure cores (each wired by deleting its inline block + a sentinel test), thin the fat bodies to orchestrators, fold the pre-3.0 read-authority split-brain (pinned kinds + a dir-equivalence proof), and honestly drain the resolution-authority census (shrink-only, enumerated cross-base artifact) on top of #2072's already-landed composite-key re-key. 9 strictly-linear WPs.
Technical Context
Language/Version: Python 3.11+ Primary Dependencies: typer, rich, ruamel.yaml, pytest, mypy (existing; no new runtime deps) Storage: files (kitty-specs planning artifacts, status.events.jsonl, git worktrees) — N/A change Testing: pytest — a golden CLI-characterization harness (extends test_tasks_cli_contract.py, adds a coord-topology + protected-branch fixture) as the parity guard + per-core unit tests (the failing-first/red-on-base artifact for these pure-parity WPs, satisfying charter C-011); selected in CI via the Wave-0 marker binding (#2294, merged) Target Platform: Linux/macOS CLI (the spec-kitty toolkit) Project Type: single (CLI tool) Performance Goals: N/A (behavior-preserving refactor — no runtime-perf target) Constraints: pure parity (100% golden cases byte-identical pre/post each WP); each command body ≤150 LOC and each extracted helper ≤150 LOC, CC ≤15; ruff + mypy clean, 0 new suppressions; net-zero new arch-ratchet entries (shrink-only floors) Scale/Scope: agent tasks surface only (9 subcommands / 53 params); ~3,617 LOC → ≤1400 LOC thin surface + cores; 9 strictly-linear work packages
Charter Check
GATE: Must pass before Phase 0. Re-checked after Phase 1. Charter .kittify/charter/charter.md (v1.2.0), governed under the #2299 compiled doctrine.
| Charter/doctrine principle | Status | Note |
|---|---|---|
| Branch/PR strategy (no direct push to main) | ✅ | plan on design/degod-tasks-2116; PR to main after #2299 |
Terminology Canon (no feature*) | ✅ | new symbols resolve_planning_read_dir, TasksPorts, CoordCommitRouter — no feature-surface |
| Code Quality (CC≤15, ruff/mypy, no suppressions) — DIRECTIVE_030 | ✅ | NFR-003 |
| C-011 ATDD-first (failing-first per WP) | ✅ (reconciled) | pure-parity WPs deliver no new observable behavior → the red-first artifact is the per-core unit test (RED against the not-yet-extracted core); the golden harness is the green parity guard (NFR-002) |
| DIRECTIVE_040 (structural intervention over point-fix) | ✅ | the "why-now": #1 change-magnet, recurring defect class |
| DIRECTIVE_041 (tests as scaffold; observable contracts; anti-vacuity) | ✅ | golden freezes observable contract; NFR-002 enumerates branches from the harness |
| DIRECTIVE_043 (shrink-only ratchet, non-vacuous gate) | ✅ | FR-011/NFR-005; C-002 preserves canonicalizer-gate non-vacuity |
| DIRECTIVE_044 (canonical-source unification) | ✅ | FR-009 (CoordRead≠CoordWrite), FR-010 (pre30 read unification) |
post-merge-arch-gate-adjudication procedure | ✅ | WP08 + mission-merge full tests/architectural/ cross-base sweep |
No charter violations. No entries in Complexity Tracking.
Project Structure
Documentation (this mission)
kitty-specs/tasks-py-degod-01KWF08S/
├── plan.md # this file
├── research.md # Phase 0 — the settled design decisions (squad-derived)
├── data-model.md # Phase 1 — port protocols + decision-core value objects
├── contracts/ # Phase 1 — port interfaces + decision-core I/O contracts
├── quickstart.md # Phase 1 — validation scenarios (golden + per-core)
└── tasks.md # Phase 2 (/spec-kitty.tasks — NOT created here)
Source Code (repository root)
src/specify_cli/cli/commands/agent/
├── tasks.py # → thin surface (target: ≤1400 LOC, 0 inline json.dumps; owned by WP09)
├── tasks_ports.py # NEW — TasksPorts protocols + Real/Fake adapters (program: FsReader, CoordCommitRouter[commit_status + commit_artifact]; local: GitOps, Render)
├── tasks_transition_core.py # NEW — pure move_task transition decision
├── tasks_mapping_core.py # NEW — pure requirement-mapping decision
├── tasks_status_view.py # NEW — pure status aggregation
├── tasks_outline.py # existing seam (from #2058/#2114)
├── tasks_materialization.py # existing seam
├── tasks_parsing_validation.py # existing seam
├── tasks_finalize_validation.py# existing seam (mark_status/finalize thin via this)
└── tasks_dependency_graph.py # existing seam
tests/specify_cli/cli/commands/agent/
├── test_tasks_cli_contract.py # golden harness (EXTENDED: coord-topology fixture + ALL move_task branches + branch-cov gate)
├── test_tasks_ports.py # NEW — port stratification + FR-010 dir-equivalence proof (WP02)
├── test_tasks_transition_core.py # NEW — per-branch unit tests (--cov-branch gated)
├── test_tasks_mapping_core.py # NEW
├── test_tasks_status_view.py # NEW
├── test_move_task_orchestration.py # NEW — WP06 sentinel + orchestration
├── test_tasks_orchestration.py # NEW — WP07/WP08 orchestration + non-import AST gate
└── test_tasks_shim.py # NEW — WP09 AST 0-json.dumps + LOC ceiling
tests/architectural/
├── resolution_gate_allowlist.yaml # census entries DRAIN (shrink) as bodies thin (FR-011); baseline 13→12
├── test_resolution_authority_gates.py # floors lowered shrink-only + margin gate (WP09; reviewer-signed cross-base artifact)
└── (NEW tasks.py LOC gate: 5 bodies ≤150, helpers ≤150, total ≤1400)
Structure Decision: single-project CLI. New sibling modules alongside tasks.py (matching the #2056 mission_* sibling-extraction template); the command file becomes a registration shim. No new packages.
Implementation Concern Map
> Concerns are architectural areas, not work packages. /spec-kitty.tasks translates them into executable WPs (the post-squad 9-WP shape is the target — WP07 is split into a core-backed slice (WP07) and a coreless slice (WP08); render+shim+census is WP09); one concern may span multiple WPs.
IC-01 — Behavior characterization (the parity anchor)
- Purpose: Freeze the exact observable
agent taskscontract before any extraction, so the refactor is provably behavior-preserving. - Relevant requirements: FR-001, C-004, NFR-001.
- Affected surfaces:
tests/specify_cli/cli/commands/agent/test_tasks_cli_contract.py(extend); a new coord-topology + protected-branch fixture class constructing real on-disk coord-worktree state. - Sequencing/depends-on: none — must be first.
- Risks: the crown-jewel coord skip-exit-0 arm and the exit-1 refuse arms are NOT covered by the existing harness (its docstring punts them); and neither are move_task's OTHER decision branches (arbiter-override, rejected-verdict, planning-artifact, review-currency, force) — the golden must freeze all of them (a from-harness branch-coverage measurement of the mutating commands is the DoD, not "skip+refuse pinned"), or WP03 extracts unguarded branches. The skip-arm assertion must be distinguishing (primary HEAD unchanged + coord event), not exit-0 + key-presence (a non-skip success also exits 0).
IC-02 — Port seam & dependency injection (stratified)
- Purpose: Define the capability boundary the cores/orchestrators depend on, shaped so Wave 2 reuses it rather than re-cutting it.
- Relevant requirements: FR-003, FR-009, C-002, C-005.
- Affected surfaces: new
tasks_ports.py; call sites intasks.py. - Sequencing/depends-on: IC-01 (freeze first); predecessor #2072 has landed (allowlist composite-keyed) → not blocked. Also delivers the FR-010 dir-equivalence proof artifact (per-kind coord-fixture equivalence) here, before any read fold.
- Risks: (1) the coord WRITE port is two capabilities over two disjoint seams —
commit_status(over the transactional emitter,GuardCapability) +commit_artifact(overcommit_for_mission,MissionArtifactKind, event-less) — NOT a fusedcommit(); the Wave-2 consumers use disjoint halves (implement=status-only,acceptance=artifact-only writer,move_task=both), so a fused method is re-cut in Wave 2 (the C-006 failure).GitOps+Renderare mission-local (#2173 DROPs). (2) inject at the_do_<cmd>(*, ports=None)orchestrator boundary, never the Typer@app.command(Protocol-param collision). (3) keep the_canonicalize_primary_read_handlefold co-located with the primitive in the adapter (intra-function gate). (4) rename result types offCommitResult(collides withgit/commit_helpers.py:424).
IC-03 — Pure decision & aggregation cores
- Purpose: Extract the logic that drives the change-ripple into pure, independently-testable functions.
- Relevant requirements: FR-002, FR-004, FR-005, FR-006.
- Affected surfaces: new
tasks_transition_core.py(move_task decision),tasks_mapping_core.py,tasks_status_view.py. - Sequencing/depends-on: IC-01, IC-02.
- Risks:
move_task's decision is a nested state machine (arbiter-override, FR-008a planning arm, force paths, review-currency, the coord skip arm) — extract it reproducing exact behavior, NOT unifying with the other commands (that's #2300, deferred). Each core's branch set is enumerated from the IC-01 golden harness, gated by--cov-branchon the core module (NFR-002). Anti-shadow-code: wiring a core into its command deletes the old inline decision block (does not add a discarded call) and ships a fake-core sentinel test proving the return value drives observable behavior — "grep-for-callers" is insufficient.
IC-04 — Command-body thinning
- Purpose: Reduce the fat bodies to thin orchestrators over cores + ports. Split across WP06 (move_task), WP07 (core-backed: map_requirements + status), WP08 (coreless: mark_status + finalize_tasks) — the squad flagged a single 4-body WP as overloaded.
- Relevant requirements: FR-007, NFR-004.
- Affected surfaces: the fat command bodies in
tasks.py(owned authoritatively by WP09; WP06-WP08 edit under documented leeway). - Sequencing/depends-on: IC-02, IC-03.
- Risks:
mark_statusandfinalize_taskscarry no new core — they thin via ports + the existingtasks_finalize_validation/parsing seams. Borrowing move_task's core would be the deferred unification (#2300) — guarded structurally by a WP08 non-import AST gate (tasks_transition_coreNOT reachable from those bodies), not behavior alone. Each body ≤150 LOC; glue helpers ≤150 LOC/CC≤15, enforced by a dedicated LOC gate (ruff CC≤15 does not bound LOC).
IC-05 — Coord read-authority unification (fold)
- Purpose: Fix the latent pre-3.0-layout read split-brain (DIRECTIVE_044).
- Relevant requirements: FR-010, C-001.
- Affected surfaces: resolver calls at
move_task:1138(WP06),finalize_tasks:2373+list_dependents:3568(WP08) — migrate kind-blindresolve_feature_dir_for_missionreads → kind-awareresolve_planning_read_dirwith a pinnedMissionArtifactKindper site. - Sequencing/depends-on: the dir-equivalence proof artifact is a WP02 deliverable (IC-02); the rewire sites ride with IC-04.
- Risks: the two resolvers differ by construction on coord topology (
-coordhusk vs primary read dir) — a wrongkindbreaks byte-identity on the WP01 coord fixture. This is the single most likely parity break; the WP02 proof (not a WP06 runtime "stop if it shifts") is the guard.
IC-06 — Render seam, shim finalization & census honesty (WP09)
- Purpose: Isolate output, reduce the file toward a thin surface (≤1400 LOC), and honestly manage the arch-ratchet census.
- Relevant requirements: FR-008, FR-011, NFR-004, NFR-005.
- Affected surfaces: the
Renderdual-arm seam (human + 13json.dumps, AST-gated to 0);tasks.py→ thin surface (owns it);tests/architectural/resolution_gate_allowlist.yaml+test_resolution_authority_gates.pyfloors + the new LOC gate. - Sequencing/depends-on: IC-04 (bodies must be thin first — that's what reclassifies WRITE→READ).
- Risks: the census is at floor (write census 12/floor 12); body-thinning reclassifies WRITE→READ → sites drain and floors lower shrink-only. Because the floor gate is a lower-bound owned by this same WP, over-lowering is self-attestable — so ship (a) a 1:1 enumerated cross-base drain artifact, (b) reviewer (not author) sign-off, (c) a margin gate. Also fix stale
coord_authority_baseline: 13→12. WP09 + the mission-merge run the fulltests/architectural/cross-base sweep (post-merge-arch-gate-adjudication).