Work Packages: Acceptance Pipeline Regression Fixes

Inputs: Design documents from kitty-specs/052-acceptance-pipeline-regression-fixes/ Prerequisites: plan.md (required), spec.md (user stories)

Tests: Regression tests required per NFR-002 — included in WP04.

Organization: Fine-grained subtasks (Txxx) roll up into work packages (WPxx). Each work package must be independently deliverable and testable.

Prompt Files: Each work package references a matching prompt file in tasks/ generated by /spec-kitty.tasks.


Work Package WP01: Verification Path Hardening (Priority: P0)

Goal: Fix the read-only verification path so collect_feature_summary() does not dirty the repo and handles corrupted event logs gracefully. Independent Test: Call collect_feature_summary() on a clean feature — git_dirty must be empty. Call it with malformed JSONL — must raise AcceptanceError, not StoreError. Prompt: tasks/WP01-verification-path-hardening.md Requirement Refs: FR-001, FR-004, FR-005

Included Subtasks

  • ✅ T001 Move git_status_lines() before materialize() in src/specify_cli/acceptance.py
  • ✅ T002 Import StoreError and wrap materialize() in try/except converting to AcceptanceError in src/specify_cli/acceptance.py
  • ✅ T003 Mirror T001+T002 in src/specify_cli/scripts/tasks/acceptance_support.py
  • ✅ T004 Sync fixes to scripts/tasks/acceptance_support.py and .kittify/scripts/tasks/acceptance_support.py

Implementation Notes

  • In collect_feature_summary(): move the git_dirty = git_status_lines(repo_root) block (currently after the materialize/WP-iteration section) to just before the # ── Canonical state validation via materialize() comment.
  • Wrap the materialize(feature_dir) call in try: ... except StoreError as exc: raise AcceptanceError(f"Status event log is corrupted for feature '{feature}': {exc}") from exc.
  • Import StoreError from specify_cli.status.store (acceptance.py) or alongside the existing materialize import (acceptance_support.py).
  • The scripts/ and .kittify/ copies should be byte-identical to the src/ copy after sync.

Parallel Opportunities

  • None — single sequential unit.

Dependencies

  • None (first WP).

Risks & Mitigations

  • Moving git_status_lines() earlier could theoretically miss dirty files created by code between old and new positions — but all intermediate code is read-only (branch detection, worktree detection).

Work Package WP02: Acceptance Metadata Persistence (Priority: P1)

Goal: Ensure perform_acceptance() writes the real commit SHA back to meta.json after creating the acceptance git commit. Independent Test: Run perform_acceptance() in a test repo → verify meta.json has non-null accept_commit and acceptance_history[-1]["accept_commit"]. Prompt: tasks/WP02-acceptance-metadata-persistence.md Requirement Refs: FR-002, FR-005

Included Subtasks

  • ✅ T005 Add post-commit SHA write-back in src/specify_cli/acceptance.py perform_acceptance()
  • ✅ T006 Mirror T005 in src/specify_cli/scripts/tasks/acceptance_support.py
  • ✅ T007 Sync to scripts/tasks/acceptance_support.py and .kittify/scripts/tasks/acceptance_support.py

Implementation Notes

``python if accept_commit: from specify_cli.feature_metadata import load_meta, write_meta meta = load_meta(summary.feature_dir) if meta is not None: meta["accept_commit"] = accept_commit history = meta.get("acceptance_history", []) if history: history[-1]["accept_commit"] = accept_commit write_meta(summary.feature_dir, meta) ``

  • After the commit is created and accept_commit is captured (after the run_git(["rev-parse", "HEAD"], ...) block), add a targeted meta.json update:
  • For acceptance_support.py (standalone): use the same pattern, but the import must work via the sys.path bootstrap (WP03 fixes this, but the import can be local/conditional).
  • Do NOT call record_acceptance() again — that would append a duplicate history entry.

Parallel Opportunities

  • None — touches same files as WP01.

Dependencies

  • Depends on WP01 (shares acceptance.py and acceptance_support.py).

Risks & Mitigations

  • The SHA update creates an uncommitted change to meta.json after the acceptance commit. This is acceptable — the SHA is reference data, not part of the commit itself.

Work Package WP03: Standalone Script Contract (Priority: P1)

Goal: Make tasks_cli.py and acceptance_support.py work when invoked directly from a repo checkout without pip install. Independent Test: python3 src/specify_cli/scripts/tasks/tasks_cli.py --help exits 0 with no ModuleNotFoundError. Prompt: tasks/WP03-standalone-script-contract.md Requirement Refs: FR-003, C-002

Included Subtasks

  • ✅ T008 Add repo src/ root to sys.path in src/specify_cli/scripts/tasks/tasks_cli.py
  • ✅ T009 Add repo src/ root to sys.path in src/specify_cli/scripts/tasks/acceptance_support.py
  • ✅ T010 Add repo src/ root to sys.path in scripts/tasks/tasks_cli.py and scripts/tasks/acceptance_support.py
  • ✅ T011 Sync .kittify/scripts/tasks/ copies from scripts/tasks/

Implementation Notes

``python _SRC_ROOT = SCRIPT_DIR.parent.parent.parent.parent # → src/ if _SRC_ROOT.name == "src" and str(_SRC_ROOT) not in sys.path: sys.path.insert(0, str(_SRC_ROOT)) ``

``python _candidate = SCRIPT_DIR for _ in range(6): _candidate = _candidate.parent _src = _candidate / "src" if (_src / "specify_cli").is_dir() and str(_src) not in sys.path: sys.path.insert(0, str(_src)) break ``

  • For src/specify_cli/scripts/tasks/ scripts: The script is at src/specify_cli/scripts/tasks/foo.py. Walking up 4 parents reaches src/. Add after existing sys.path.insert(0, str(SCRIPT_DIR)):
  • For scripts/tasks/ scripts: Different depth — at repo root level. Use a bounded walk:
  • For acceptance_support.py: Add the bootstrap BEFORE the from specify_cli.status.reducer import materialize line — that import is what fails without it.

Parallel Opportunities

  • None — sequential with WP01/WP02.

Dependencies

  • Depends on WP01 (WP01's StoreError import in acceptance_support.py requires the sys.path fix to work standalone).

Risks & Mitigations

  • Parent walk may not find src/ in unusual layouts → bounded at 6 levels + name guard.
  • If spec-kitty IS pip-installed, adding src/ to sys.path could shadow the installed package → sys.path.insert(0, ...) intentionally prefers local source, which is correct for development.

Work Package WP04: Regression Coverage and Copy-Parity Sweep (Priority: P2)

Goal: Add focused regression tests for all 4 bugs and verify dual-copy alignment between acceptance.py and acceptance_support.py. Independent Test: pytest tests/specify_cli/test_acceptance_regressions.py -v — all pass. Prompt: tasks/WP04-regression-coverage-parity.md Requirement Refs: NFR-002, NFR-003, C-001, C-003

Included Subtasks

  • ✅ T012 Test: collect_feature_summary() does not dirty the repo via materialize() (P0 regression)
  • ✅ T013 Test: perform_acceptance() persists accept_commit to meta.json (P1 regression)
  • ✅ T014 Test: standalone tasks_cli.py --help succeeds via subprocess.run() (P1 regression)
  • ✅ T015 Test: malformed status.events.jsonl raises AcceptanceError not StoreError (P2 regression)
  • ✅ T016 Copy-parity assertions between acceptance.py and acceptance_support.py

Implementation Notes

  • Create tests/specify_cli/test_acceptance_regressions.py with all 5 tests.
  • T012: Create a temp git repo with a feature, valid event log, all WPs done. Call collect_feature_summary(). Assert summary.git_dirty is empty.
  • T013: Use a real or mocked acceptance flow. After perform_acceptance(), read meta.json and assert accept_commit is non-null.
  • T014: Use subprocess.run(["python3", str(script_path), "--help"], capture_output=True). Assert returncode 0 and "ModuleNotFoundError" not in stderr.
  • T015: Write invalid JSON to status.events.jsonl. Call collect_feature_summary(). Assert pytest.raises(AcceptanceError) and the message contains "corrupted".
  • T016: Compare __all__ exports from both modules. Verify key function signatures match.

Parallel Opportunities

  • None — must run after WP01-WP03 are applied.

Dependencies

  • Depends on WP03 (all fixes must be in place for tests to pass).

Risks & Mitigations

  • T014 may be environment-sensitive (pip install vs checkout) → control PYTHONPATH in subprocess env.
  • Pre-existing test failures (~50) are known — new tests must pass in isolation.

Dependency & Execution Summary

  • Sequence: WP01 → WP02 → WP03 → WP04
  • Parallelization: None — all WPs touch overlapping files.
  • MVP Scope: WP01 alone unblocks acceptance (P0 fix). WP01+WP02+WP03 fixes all functional bugs.

Requirements Coverage Summary

Requirement IDCovered By Work Package(s)
FR-001WP01
FR-002WP02
FR-003WP03
FR-004WP01
FR-005WP01, WP02
NFR-001All (no new deps)
NFR-002WP04
NFR-003WP04
C-001WP01, WP02, WP03, WP04
C-002WP03
C-003WP04
C-004All

Subtask Index (Reference)

Subtask IDSummaryWork PackagePriorityParallel?
T001Move git_status_lines before materialize in acceptance.pyWP01P0No
T002Wrap materialize in StoreError handler in acceptance.pyWP01P0No
T003Mirror T001+T002 in src/ acceptance_support.pyWP01P0No
T004Sync to scripts/ and .kittify/ copiesWP01P0No
T005Add post-commit SHA write-back in acceptance.pyWP02P1No
T006Mirror T005 in src/ acceptance_support.pyWP02P1No
T007Sync to scripts/ and .kittify/ copiesWP02P1No
T008sys.path bootstrap in src/ tasks_cli.pyWP03P1No
T009sys.path bootstrap in src/ acceptance_support.pyWP03P1No
T010sys.path bootstrap in scripts/ copiesWP03P1No
T011Sync .kittify/ copiesWP03P1No
T012Test: materialize ordering doesn't dirty repoWP04P2No
T013Test: accept_commit persisted to meta.jsonWP04P2No
T014Test: standalone tasks_cli.py --help worksWP04P2No
T015Test: malformed JSONL raises AcceptanceErrorWP04P2No
T016Copy-parity assertionsWP04P2No

<!-- status-model:start -->

Canonical Status (Generated)

<!-- status-model:end -->

  • WP01: done
  • WP02: done
  • WP03: done
  • WP04: done