Implementation Plan: Planning-artifact WPs Own kitty-specs Paths
Branch: feat/3222-2643-kitty-specs-ownership | Date: 2026-08-18 | Spec: spec.md Input: Feature specification from kitty-specs/planning-artifact-kitty-specs-ownership-01M0AEV7/spec.md
Summary
Narrow the finalize-tasks "owned_files cannot include paths under kitty-specs/" ban so it exempts execution_mode: planning_artifact work packages while staying fail-closed for code_change. The ownership model already blesses kitty-specs/ ownership for planning artifacts (ownership/validation.py _PLANNING_PREFIXES + validate_execution_mode_consistency), and the lane layer already routes such work packages to the repo-root planning lane; only the finalize ban disagrees. The technical approach is a single execution-mode guard in the ban predicate (_invalid_mission_specs_owned_files, cli/commands/agent/mission_parsing.py), with red-first acceptance coverage and a preserved fail-closed floor. No other production surface changes — verified end-to-end by the research squad (workspace resolution, lane guards, manifest build, and lane computation all already accept the planning-artifact-owns-kitty-specs shape).
Technical Context
Language/Version: Python 3.11+ Primary Dependencies: None new. Touches existing internal surfaces only — specify_cli.cli.commands.agent.mission_parsing, mission_finalize, and (read-only, no change) specify_cli.ownership.validation, specify_cli.lanes.compute. Storage: N/A — WP frontmatter (YAML) under kitty-specs/<mission>/tasks/; no datastore. Testing: pytest (targeted, per the parallel-run rules in CLAUDE.md); ruff and mypy --strict on changed files. Red-first acceptance test drives the change. Target Platform: Spec Kitty CLI (Linux/macOS developer environments). Project Type: single (Python CLI package under src/specify_cli/). Performance Goals: N/A — the guard runs once per WP at finalize time; the added check is an equality comparison per WP. Constraints: Every changed/added function stays at cyclomatic complexity ≤ 15; no # noqa / # type: ignore / Sonar suppressions; preserve the dynamic-alias patch seam _invalid_kitty_specs_owned_files; the exemption keys strictly on execution_mode == planning_artifact; the code_change ban stays fail-closed. Scale/Scope: One predicate guard + focused tests; ~1 file of production change plus test additions/updates.
Supply-chain security: Not applicable — this plan adds, upgrades, and removes no dependencies in any ecosystem. No research.md supply-chain decision or lifecycle-script review is required.
Charter Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
- Single canonical authority (
DIRECTIVE_044): PASS — the fix alignsfinalize-tasksto the existing ownership authority (validate_execution_mode_consistency/_PLANNING_PREFIXES) rather than adding a second rule. It removes a divergent, later over-reach; it does not introduce a competing surface. - Architectural alignment / gate discipline (
DIRECTIVE_001,DIRECTIVE_043): PASS — thekitty-specs/ban remains a non-vacuous fail-closed gate forcode_change; only theplanning_artifactcase (which cannot reach a lane branch) is exempted. - ATDD-first (
acceptance-test-first): PASS — driven by a red-first finalize acceptance test (reuse #2643's YAML) plus a fail-closed floor. - Tiered rigour / tests-with-branches: PASS — the single new branch (the exemption) is covered by a direct predicate unit test and an end-to-end finalize acceptance test in the same change.
- Terminology canon: PASS — "Mission" / "work package" vocabulary; no
feature*aliases introduced. - Campsite discipline (
DIRECTIVE_025): the touched surfaces (mission_parsing.py,mission_finalize.py) are scanned for domain-matched debt at implementation; no god-surface refactor is anticipated (the change is a single guard).
No violations → no Complexity Tracking entries required.
Project Structure
Documentation (this mission)
kitty-specs/planning-artifact-kitty-specs-ownership-01M0AEV7/
├── plan.md # This file
├── research.md # Phase 0 — decision consolidation (squad findings)
├── data-model.md # Phase 1 — WP ownership entities + verdict table
├── quickstart.md # Phase 1 — how to reproduce/verify
├── contracts/
│ └── finalize-ownership-contract.md # Phase 1 — finalize-tasks kitty-specs ownership decision contract
└── tasks.md # Phase 2 (/spec-kitty.tasks — NOT created here)
Source Code (repository root)
src/specify_cli/cli/commands/agent/
├── mission_parsing.py # CHANGE: _invalid_mission_specs_owned_files gains a planning_artifact exemption
└── mission_finalize.py # CONFIRM: _validate_owned_files_not_in_mission_specs call-site reads post-bootstrap execution_mode
src/specify_cli/ownership/validation.py # READ-ONLY: the authoritative model the fix aligns to (no change)
src/specify_cli/lanes/compute.py # READ-ONLY: planning-lane routing already accepts the shape (no change)
tests/
├── specify_cli/cli/commands/agent/test_mission_parsing.py # UPDATE: predicate unit tests gain execution_mode context
├── specify_cli/cli/commands/agent/test_mission_finalize_phases.py # UPDATE: direct-call unit tests supply execution_mode
├── tasks/test_finalize_tasks_owned_files_validation.py # KEEP GREEN (code_change fail-closed floor) + ADD positive planning_artifact case
└── (new/extended) finalize acceptance test reusing #2643's YAML
Structure Decision: Single Python package (src/specify_cli/). The change is localized to the agent command family; the ownership and lanes packages are consulted read-only to confirm the downstream already accepts the shape.
Complexity Tracking
No Charter Check violations — table intentionally empty.
Implementation Concern Map
> Implementation concerns are NOT work packages. /spec-kitty.tasks translates these into executable WPs.
> Post-plan squad refinements folded in — see squad-findings-post-plan.md. The exemption gains a confinement condition (R-1); the acceptance test must clear two downstream hard-gates (D-1); durability is filename-scoped (D-2); three extra negative/inference cases are added (A-1, A-2, D-3).
IC-01 — Narrow the finalize kitty-specs ban to code_change (confined to planning ownership)
- Purpose: Make the
finalize-taskskitty-specs/owned-files ban execution-mode-aware so aplanning_artifactwork package may ownkitty-specs/deliverables, whilecode_changestays fail-closed. - Relevant requirements: FR-001, FR-002, FR-003, FR-004, FR-005.
- Affected surfaces:
src/specify_cli/cli/commands/agent/mission_parsing.py(_invalid_mission_specs_owned_files);src/specify_cli/cli/commands/agent/mission_finalize.py(_validate_owned_files_not_in_mission_specscall-site confirmation). Dynamic alias_invalid_kitty_specs_owned_filespreserved. - Exemption condition (refined, R-1): skip a WP only when
execution_mode == planning_artifactAND everyowned_filesentry is under_PLANNING_PREFIXES(import the canonical constant fromspecify_cli.ownership.validation— single authority, do not re-derive). This confinement means aplanning_artifactWP that also ownssrc//tests/is not exempted (closes the mislabel-owns-code hole). Compare againstExecutionMode.PLANNING_ARTIFACT.value/ normalized (R-3), not incidentalStrEnumequality. - Sequencing/depends-on: none.
- Risks: preserve the monkeypatch/alias seam; the unset-mode path stays fail-closed (inference decides, and any code signal →
code_change). Confirm the predicate reads the post-bootstrapexecution_mode(the ban atmission_finalize.py:2069runs after_apply_ownership_inference). Keep the added condition at complexity ≤ 15 (extract a small helper if needed).
IC-02 — Acceptance, fail-closed floor, and regression coverage
1. Positive acceptance (D-1) — planning_artifact owning only kitty-specs/<slug>/… finalizes cleanly and lands in the planning lane. MUST clear the two downstream hard-gates: build it inference-driven (empty owned_files + planning-only body → inferred kitty-specs ownership + authoritative_surface, glob matches existing spec.md/plan.md) or set a kitty-specs authoritative_surface + create_intent. Assert finalize passes _validate_ownership_manifests (mission_finalize.py:2085) and wp_id in compute_lanes(...).planning_artifact_wps (D-5). 2. Fail-closed floor (FR-003) — existing code_change + kitty-specs ban tests stay green (fixtures already pin code_change). 3. Confinement (R-1) — planning_artifact owning kitty-specs/… and src/… is still rejected. 4. Overlap floor (A-1) — two planning_artifact WPs with overlapping kitty-specs/ scopes and no dep edge are still rejected by validate_no_overlap. 5. Inferred-planning ACCEPT (A-2) — unset mode + kitty-specs-only body infers planning_artifact and is accepted (pins the inference→ban ordering). 6. Inferred-code REJECT (D-3) — unset mode + a src//.py code signal in the body infers code_change; assert the resolved execution_mode == code_change, then assert INVALID_WP_OWNED_FILES_KITTY_SPECS. 7. Predicate unit tests (blast radius) — update test_mission_parsing.py / test_mission_finalize_phases.py direct-call tests to supply execution_mode; assert the alias/shim identity is preserved.
- Purpose: Prove the positive case end-to-end, lock every guardrail the exemption newly exposes, and bind the regression guards to concrete seams (not fragile integration tests).
- Relevant requirements: FR-006, NFR-001, C-003.
- Affected surfaces:
tests/specify_cli/cli/commands/agent/test_mission_parsing.py,test_mission_finalize_phases.py,tests/tasks/test_finalize_tasks_owned_files_validation.py, plus an end-to-end finalize acceptance test reusing #2643's reproduction shape. - Test set (each red-first where it exercises IC-01):
- Regression guards bound to seams (D-4, D-2):
authoritative_surface— exerciseinfer_authoritative_surface(ownership/inference.py:154) →validate_authoritative_surface; assert a kitty-specs owned file yields a compatible surface.- Durability is filename-scoped — exercise
_is_coordination_owned_artifact/kind_for_mission_file(lanes/auto_rebase.py:236): assert akitty-specs/<slug>/disposition-matrix.mddeliverable is durable (kind is None) and a NEGATIVE assertion thatanalysis-report.md/tasks/WP*.mdis a managed kind (documented carve-out — C-003 holds only for non-managed filenames). - Sequencing/depends-on: exercises IC-01 (cases 1, 3, 5 are red before IC-01 lands).
Follow-up (out of this mission, A-4)
File a tech-debt ticket for the duplicated topology rule between policy/commit_guard.py:88 (runtime) and the finalize kitty-specs ban (plan-time) — a shared can_lane_commit(path, mode) predicate would host it once. Not a blocker; predicate-local is the correct minimal fix here.