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()beforematerialize()insrc/specify_cli/acceptance.py - ✅ T002 Import
StoreErrorand wrapmaterialize()in try/except converting toAcceptanceErrorinsrc/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.pyand.kittify/scripts/tasks/acceptance_support.py
Implementation Notes
- In
collect_feature_summary(): move thegit_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 intry: ... except StoreError as exc: raise AcceptanceError(f"Status event log is corrupted for feature '{feature}': {exc}") from exc. - Import
StoreErrorfromspecify_cli.status.store(acceptance.py) or alongside the existingmaterializeimport (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.pyperform_acceptance() - ✅ T006 Mirror T005 in
src/specify_cli/scripts/tasks/acceptance_support.py - ✅ T007 Sync to
scripts/tasks/acceptance_support.pyand.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_commitis captured (after therun_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.pyandacceptance_support.py).
Risks & Mitigations
- The SHA update creates an uncommitted change to
meta.jsonafter 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 tosys.pathinsrc/specify_cli/scripts/tasks/tasks_cli.py - ✅ T009 Add repo
src/root tosys.pathinsrc/specify_cli/scripts/tasks/acceptance_support.py - ✅ T010 Add repo
src/root tosys.pathinscripts/tasks/tasks_cli.pyandscripts/tasks/acceptance_support.py - ✅ T011 Sync
.kittify/scripts/tasks/copies fromscripts/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 atsrc/specify_cli/scripts/tasks/foo.py. Walking up 4 parents reachessrc/. Add after existingsys.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 thefrom specify_cli.status.reducer import materializeline — 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 viamaterialize()(P0 regression) - ✅ T013 Test:
perform_acceptance()persistsaccept_committometa.json(P1 regression) - ✅ T014 Test: standalone
tasks_cli.py --helpsucceeds viasubprocess.run()(P1 regression) - ✅ T015 Test: malformed
status.events.jsonlraisesAcceptanceErrornotStoreError(P2 regression) - ✅ T016 Copy-parity assertions between
acceptance.pyandacceptance_support.py
Implementation Notes
- Create
tests/specify_cli/test_acceptance_regressions.pywith all 5 tests. - T012: Create a temp git repo with a feature, valid event log, all WPs done. Call
collect_feature_summary(). Assertsummary.git_dirtyis empty. - T013: Use a real or mocked acceptance flow. After
perform_acceptance(), readmeta.jsonand assertaccept_commitis 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. Callcollect_feature_summary(). Assertpytest.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 ID | Covered By Work Package(s) |
|---|---|
| FR-001 | WP01 |
| FR-002 | WP02 |
| FR-003 | WP03 |
| FR-004 | WP01 |
| FR-005 | WP01, WP02 |
| NFR-001 | All (no new deps) |
| NFR-002 | WP04 |
| NFR-003 | WP04 |
| C-001 | WP01, WP02, WP03, WP04 |
| C-002 | WP03 |
| C-003 | WP04 |
| C-004 | All |
Subtask Index (Reference)
| Subtask ID | Summary | Work Package | Priority | Parallel? |
|---|---|---|---|---|
| T001 | Move git_status_lines before materialize in acceptance.py | WP01 | P0 | No |
| T002 | Wrap materialize in StoreError handler in acceptance.py | WP01 | P0 | No |
| T003 | Mirror T001+T002 in src/ acceptance_support.py | WP01 | P0 | No |
| T004 | Sync to scripts/ and .kittify/ copies | WP01 | P0 | No |
| T005 | Add post-commit SHA write-back in acceptance.py | WP02 | P1 | No |
| T006 | Mirror T005 in src/ acceptance_support.py | WP02 | P1 | No |
| T007 | Sync to scripts/ and .kittify/ copies | WP02 | P1 | No |
| T008 | sys.path bootstrap in src/ tasks_cli.py | WP03 | P1 | No |
| T009 | sys.path bootstrap in src/ acceptance_support.py | WP03 | P1 | No |
| T010 | sys.path bootstrap in scripts/ copies | WP03 | P1 | No |
| T011 | Sync .kittify/ copies | WP03 | P1 | No |
| T012 | Test: materialize ordering doesn't dirty repo | WP04 | P2 | No |
| T013 | Test: accept_commit persisted to meta.json | WP04 | P2 | No |
| T014 | Test: standalone tasks_cli.py --help works | WP04 | P2 | No |
| T015 | Test: malformed JSONL raises AcceptanceError | WP04 | P2 | No |
| T016 | Copy-parity assertions | WP04 | P2 | No |
<!-- status-model:start -->
Canonical Status (Generated)
<!-- status-model:end -->
- WP01: done
- WP02: done
- WP03: done
- WP04: done