Implementation Plan: CLI Bug Sweep & Tool Surface Self-Registration

Branch: fix/cli-bug-sweep-tool-surface-self-registration | Date: 2026-06-15 | Spec: spec.md Input: Feature specification from kitty-specs/cli-bug-sweep-tool-surface-self-registration-01KV5AWE/spec.md

Summary

Six targeted fixes across four subsystems. The largest is a structural refactor of tool_surface/service.py into a self-registration model (IC-04). Three others are small: remove a stale test marker (IC-01), add a missing test case for branch naming (IC-02), and repair three interlocking charter validation defects (IC-03). IC-05 is two task-workflow DX fixes: correct map-requirements path resolution and improve finalize-tasks --validate-only error messaging. All five ICs can be implemented in parallel lanes; there are no cross-IC dependencies.

Technical Context

Language/Version: Python 3.11+ Primary Dependencies: typer, rich, ruamel.yaml, pytest, mypy (strict) Storage: N/A — no persistence layer changes Testing: pytest with --dist loadfile for parallel runs; mypy --strict zero errors; ruff zero issues; 90%+ branch coverage on new code; Directive-030 conformance test for IC-04 Target Platform: Linux, macOS, Windows 10+ (CLI tool) Project Type: Single Python CLI package (src/specify_cli/ + src/charter/) Performance Goals: N/A — no performance-sensitive paths affected Constraints: No pkgutil/filesystem scan in provider discovery (C-001); no new user-facing CLI surfaces (C-004); provider ordering deterministic via explicit integer field (FR-010); gitignore whitelist must not be changed (C-003) Scale/Scope: ~16 files modified or created; largest change is IC-04 at ~10 files

Charter Check

GateStatusNotes
DIRECTIVE_003 — Decision documentationPassSurfaceRegistration shape documented in research.md and data-model.md
DIRECTIVE_005 — Tests for new functionalityPassEach IC includes test coverage; IC-04 adds Directive-030 conformance test
DIRECTIVE_006 — mypy --strictPassEnforced as part of NFR-001
DIRECTIVE_010 — Spec fidelityPassAll FR/NFR/C mapped to specific ICs; no undocumented deviations
DIRECTIVE_010/011 — Identifier sanitizationN/ANo sanitizer changes in scope
DIRECTIVE_030 — No central provider literalsPassIC-04 conformance test asserts coordinator file has no central provider list
Complexity ceiling (15)PassIC-04 introduces helper functions scoped to stay ≤15

No charter violations requiring justification.

Project Structure

Documentation (this mission)

kitty-specs/cli-bug-sweep-tool-surface-self-registration-01KV5AWE/
├── plan.md              # This file
├── research.md          # Debugger Debbie findings summary
├── data-model.md        # SurfaceRegistration entity design
├── quickstart.md        # Validation guide
└── tasks.md             # Generated by /spec-kitty.tasks

Source Code (affected paths)

src/specify_cli/
├── lanes/
│   └── branch_naming.py              # IC-02: docstring update only
├── tool_surface/
│   ├── service.py                    # IC-04: refactored to consume registry
│   └── providers/
│       ├── _registry.py              # IC-04: NEW — SurfaceRegistration + registry
│       ├── _discovery.py             # IC-04: NEW — explicit import tuple
│       ├── agent_profiles.py         # IC-04: add registration call
│       ├── command_skills.py         # IC-04: add registration call
│       ├── managed_skills.py         # IC-04: add registration call
│       ├── native_config.py          # IC-04: add registration call
│       ├── plugin_bundle.py          # IC-04: add registration call
│       ├── session_presence.py       # IC-04: add registration call
│       └── slash_commands.py         # IC-04: add registration call

src/charter/
├── bundle.py                         # IC-03: built_in_only early-exit
└── synthesizer/
    └── artifact_naming.py            # IC-03: singular subdir names

tests/
├── adversarial/
│   └── test_distribution.py          # IC-01: remove xfail decorator
├── core/
│   └── test_branch_naming_human_slug.py  # IC-02: pathological case test
└── specify_cli/
    ├── charter/
    │   └── test_bundle_validate.py   # IC-03: built_in_only scenario test
    └── tool_surface/
        └── test_provider_registration.py  # IC-04: NEW — conformance test

.kittify/charter/provenance/          # IC-03: git rm 7 stale sidecar files

src/specify_cli/
├── cli/commands/agent/tasks.py       # IC-05a: map-requirements spec.md path resolution
└── ownership/
    └── validation.py                 # IC-05b: create_intent hint for zero-match literal paths

Implementation Concern Map

> Implementation concerns are NOT work packages. /spec-kitty.tasks translates > these into executable WPs. Concerns may be parallelized — there are no > cross-IC dependencies in this mission.

IC-01 — Stale xfail removal

  • Purpose: Remove the @pytest.mark.xfail(strict=False) decorator on TestUpgradeWithAllMissions::test_upgrade_updates_templates in tests/adversarial/test_distribution.py so the test provides real regression protection for init-with---ai.
  • Relevant requirements: FR-001
  • Affected surfaces: tests/adversarial/test_distribution.py lines 193–206 (decorator + comment block)
  • Sequencing/depends-on: none
  • Risks: If ensure_runtime() is flaky in CI after the marker is removed, that failure will be visible immediately. It is a pre-existing latent issue, not introduced here; file a new issue if it manifests rather than re-adding the xfail.

IC-02 — Branch naming pathological test and docstring

  • Purpose: Add a test covering the pathological slug-contains-wrong-mid8 case to tests/core/test_branch_naming_human_slug.py, and add an invariant docstring to _human_slug_for_mid8_branch() in src/specify_cli/lanes/branch_naming.py.
  • Relevant requirements: FR-002
  • Affected surfaces: tests/core/test_branch_naming_human_slug.py (new parameterized case); src/specify_cli/lanes/branch_naming.py:134 (docstring)
  • Sequencing/depends-on: none
  • Risks: The test must assert the current behavior (double-append in the pathological case) as documented behavior — not as a bug to fix — so any future change is caught explicitly.

IC-03 — Charter bundle validation repair (three-part)

  • Purpose: Restore spec-kitty charter bundle validate to exit 0 on a fresh checkout by (A) removing 7 stale provenance sidecar files, (B) correcting doctrine_kind_subdir() to use singular directory names matching the gitignore whitelist, and (C) adding a built_in_only early-exit to validate_synthesis_state().
  • Relevant requirements: FR-003, FR-004, FR-005
  • Affected surfaces:
  • Part A: git rm on .kittify/charter/provenance/ (7 files listed in spec)
  • Part B: src/charter/synthesizer/artifact_naming.pydoctrine_kind_subdir() and all callers in write_pipeline.py
  • Part C: src/charter/bundle.pyvalidate_synthesis_state(); add test in tests/specify_cli/charter/
  • Sequencing/depends-on: none; B and C can be done in any order after A
  • Risks: Part B must audit all callers of doctrine_kind_subdir() and any hardcoded plural-dir paths in write_pipeline.py (lines ~174, ~206, ~584 per Debugger Debbie). Missing a caller leaves a dangling plural path. Part C must not suppress validation for repos that have real synthesis state — the early-exit is gated on built_in_only: true AND artifacts == [].

IC-04 — Tool surface self-registration seam

  • Purpose: Eliminate the four-region centralized provider enrollment in service.py by introducing a SurfaceRegistration dataclass and a SurfaceProviderRegistry class store. Each provider module declares its own registration; service.py derives all configuration from the registry.
  • Relevant requirements: FR-006, FR-007, FR-008, FR-009, FR-010
  • Affected surfaces: See project structure — 2 new files, 7 provider modules modified, service.py refactored, 1 new conformance test
  • Sequencing/depends-on: none; operates in an independent subsystem
  • Risks:
  • session_presence contributes 3 definitions (not 1): SurfaceRegistration.definitions must be a tuple, not a single field.
  • plugin_bundle registers under a synthetic key, not the per-tool-key fan-out: SurfaceRegistration.synthetic_key: str | None field required.
  • plugin_bundle.py has a lazy import of build_plans_for_bundles from service.py to avoid a circular import. This workaround must be preserved unchanged after the refactor; do not attempt to resolve the cycle as part of this IC.
  • _discovery.py must use an explicit tuple (not pkgutil) to satisfy the dead-symbol gate (C-001).
  • SurfaceRegistration.order must be an explicit integer (not derived from import order) to satisfy FR-010.
  • The Directive-030 conformance test (test_provider_registration.py) must assert two things: (1) every non-underscore module in providers/ has a corresponding registration, and (2) service.py contains no central provider literal list patterns.

IC-05 — Task-workflow DX fixes (two-part)

  • Purpose: Fix two workflow bugs discovered during this mission's own planning phase that block the documented map-requirementsfinalize-tasks flow and produce misleading error messages.
  • Relevant requirements: FR-011, FR-012
  • Affected surfaces:
  • Part A (src/specify_cli/cli/commands/agent/tasks.pymap_requirements function): map-requirements calls resolve_feature_dir_for_mission(main_repo_root, mission_slug), which delegates to mission_runtime.resolve_action_context. The runtime's coord-worktree routing fires only when meta.json carries coordination_branch — absent for PR-bound missions. The runtime falls through to the primary checkout, where kitty-specs/<mission>/ may not exist if the mission's target branch is checked out in a coord worktree, causing the "spec.md not found" error. Fix (investigation-first — see WP05 T020 for the confirmed approach): Route map_requirements through resolve_feature_dir_for_slug (which calls _read_path_resolver.resolve_mission_read_path directly and is already coord-topology-aware, bypassing the coordination_branch gate). Confirm via source tracing before implementing — see WP05 T020 for detailed investigation guidance.
  • Part B (src/specify_cli/ownership/validation.pyvalidate_glob_matches, around line 374): when a zero-match literal owned_files entry has a nearest-match suggestion (_nearest_match_suggestion returns non-None), the error message includes the "did you mean?" hint but omits the create_intent guidance. The two paths are mutually exclusive (if suggestion: msg += suggestion; else: msg += create_intent_hint). Fix: change to if suggestion: msg += suggestion; msg += create_intent_hint so both appear together when a nearest-match is found.
  • Sequencing/depends-on: none; both parts touch independent code surfaces
  • Risks:
  • Part A must not regress the case where the primary checkout IS on the target branch (no coord worktree). Test both paths.
  • Part B's message change must be covered by an updated test in the existing ownership validation test suite. Changing error message text may break test assertions that compare against the old exact string — audit tests/ for affected assertions before merging.