Work Packages: Coord-Shadows Follow-ups Closeout
Inputs: Design documents from kitty-specs/coord-shadows-followups-01KXBCZ1/ Prerequisites: plan.md (required), spec.md (user stories)
Tests: Required — this mission's value is correctness/robustness; every behavior-changing WP carries a red-first or characterization test as a first-class subtask.
Organization: Fine-grained subtasks (Txxx) roll up into work packages (WPxx). Each WP is independently deliverable and testable. 1 IC → 1 WP (post-plan squad confirmed; do not split).
Subtask Format: [Txxx] [P?] Description
- [P] indicates the subtask can proceed in parallel (different files/components).
Path Conventions
- Single project:
src/specify_cli/,tests/specify_cli/.
Work Package WP01: Subtask-gate single seam (Priority: P1)
Goal: Collapse the three divergent subtask-gate-dir resolvers into one canonical resolve_subtasks_gate_dir with the strong git-ancestry fallback, so no call site gates on a stale coordination husk. Independent Test: On a git-rooted coord-topology fixture with repo_root=None, all three call sites resolve the PRIMARY tasks.md; the two already-strong sites are byte-identical to today; a bare tmp_path falls back to feature_dir. Prompt: /tasks/WP01-subtask-gate-single-seam.md Requirement Refs: FR-001, FR-002, FR-003, NFR-001, NFR-002, C-002, C-003
Included Subtasks
- ✅ T001 [P] Red-first test: prove
coordination/status_transition.py::_prepare_eventwithrepo_root=Noneon a git-rooted coord fixture currently resolves the coord husk (failing assertion for the intended primary resolution) intests/specify_cli/status/test_subtasks_gate_dir_seam.py - ✅ T002 Add
resolve_subtasks_gate_dir(feature_dir, repo_root, mission_slug) -> Pathtosrc/specify_cli/missions/_read_path_resolver.py(emit.py's superset contract:repo_root→resolve_canonical_root→feature_dironWorkspaceRootNotFound; carry thecast(Path, ...)verbatim per C-002) - ✅ T003 Repoint
src/specify_cli/status/emit.py: delete_resolve_primary_subtasks_dir, route both callers (L581, L737) through the seam - ✅ T004 Repoint
src/specify_cli/status/aggregate.py::_resolve_review_gate_inputsinline resolver to the seam - ✅ T005 Repoint
src/specify_cli/coordination/status_transition.py::_prepare_eventinline resolver to the seam (this grants the strong fallback and makes T001 green) - ✅ T006 [P] Characterization test: the two strong sites (emit, aggregate) produce byte-identical resolution for pre-existing inputs; non-git
tmp_pathfalls back tofeature_dirunchanged - ✅ T007 Verify dead-code gate clean (no orphaned
_resolve_primary_subtasks_dir);ruff+mypyzero new findings
Dependencies
- None (independent spine keystone).
Risks & Mitigations
status_transition.pychange is a behavior fix → T001 red-first proves it; T006 pins the strong sites. Dead-code gate flags an incomplete repoint (T007).
Work Package WP02: PID-reuse-aware liveness + truth-in-labeling (Priority: P1)
Goal: Make is_process_alive unfoolable by a recycled PID via a persisted creation-time baseline co-written at every claim site, and correct the mislabeled test + docstring overclaim. Degradation is additive — absent baseline preserves today's behavior (zero legacy regression). Independent Test: A simulated baseline mismatch is treated as not-alive → falls to the timestamp heuristic; a workflow_executor-claimed WP carries the baseline; a legacy (absent-baseline) claim preserves live-PID trust; is_process_alive(pid)->bool signature unchanged. Prompt: /tasks/WP02-pid-reuse-liveness.md Requirement Refs: FR-004, FR-005, FR-006, NFR-003, NFR-004, NFR-005, C-005, C-007
Included Subtasks
- ✅ T008 [P] Truth-in-labeling (independent of baseline work, D4): fix the
process_liveness.pymodule/function docstring overclaim about "recycled PIDs"; rename the mislabeled testtest_recycled_pid_generic_exception_returns_falseto describe the current blindness it actually exercises - ✅ T009 Register the additive baseline field (process creation-time) in
src/specify_cli/frontmatter.py; model it insrc/specify_cli/status/wp_metadata.py - ✅ T010 Extract ONE claim-write helper that co-writes
shell_pid+ the creation-time baseline (close-by-construction, D3b) - ✅ T011 Route
src/specify_cli/cli/commands/implement.py(~L1400) through the helper - ✅ T012 Route
src/specify_cli/cli/commands/agent/workflow_executor.pyimplement-claim (~L668) AND review-claim (~L1338) through the helper (both write theshell_pidkey staleness reads) - ✅ T013 Add baseline-aware compare in
src/specify_cli/core/process_liveness.py(companionis_claiming_process_alive(pid, baseline)or optional param, keepingis_process_alive(pid)->boolfrozen) and wiresrc/specify_cli/core/stale_detection.py::_is_claiming_process_aliveto pass + compare it; absent baseline preserves today's live-PID trust (D3a) - ✅ T014 [P] Test: simulated baseline mismatch → not-alive → timestamp-heuristic fallback (the deterministic seam) in
tests/specify_cli/core/test_process_liveness.py - ✅ T015 [P] Test: real spawn→kill liveness path
- ✅ T016 [P] Test: a WP claimed via
workflow_executorcarries the baseline (regression guard for the primary implement-loop claim path) - ✅ T017 [P] Test: legacy claim (absent baseline) preserves live-PID trust (no regression)
- ✅ T018 Verify C-005 (no psutil-consumer sweep beyond process_liveness + stale_detection) and C-007 (one additive field);
ruff+mypyzero new findings
Dependencies
- None.
Risks & Mitigations
- Scope-creep magnet → C-005/C-007 fence + T018 verify. Primary-loop regression → T012 co-writes all sites + T016 guards it. Deterministic testing → T014 mismatch seam, not an OS PID-recycle.
Work Package WP03: Guarded rollback-uncheck (Priority: P2)
Goal: Ensure a WP rolled back to planned reliably loses its - [x] rows even when the out-of-lock write errors, so #2513 cannot silently re-manifest — without folding under the status lock. Independent Test: A simulated write failure in the rollback-uncheck window does not silently leave - [x] rows on a planned WP (the failure is surfaced in the result); the write routes through write_text_within_directory; out-of-lock ordering and review-lock release are preserved. Prompt: /tasks/WP03-guarded-rollback-uncheck.md Requirement Refs: FR-007, C-001, C-004
Included Subtasks
- ✅ T019 [P] Failure-mode test: simulate a write failure in
_mt_uncheck_rollback_subtasksand assert rows are NOT silently left checked on aplannedWP and themove-taskresult reflects the incomplete rollback, intests/specify_cli/cli/commands/agent/test_move_task_rollback_uncheck_robust.py - ✅ T020 Route the uncheck read/write through
write_text_within_directory(house guard) insrc/specify_cli/cli/commands/agent/tasks_move_task.py::_mt_uncheck_rollback_subtasks - ✅ T021 Implement the surfaced-not-swallowed failure mode (record incomplete-rollback on
_MoveTaskState/ result envelope + error log) WITHOUT aborting_mt_release_review_lock - ✅ T022 [P] Test: out-of-lock ordering preserved (uncheck runs after the status lock exits; review-lock release still fires on failure)
- ✅ T023 Confirm
owned_filesstays at the_mt_uncheck_rollback_subtasksseam only (C-004:_mt_run_pre_review_gate/#2573 shares the module);ruff+mypyzero new findings
Dependencies
- None.
Risks & Mitigations
- Must NOT fold under
feature_status_lock(C-001) nor touch_mt_run_pre_review_gate(C-004). Surface must not abort lock release → T022 pins ordering.
Work Package WP04: Checkbox-parser canonicalization (Priority: P3)
Goal: Replace the acceptance gate's stray whole-file [ ] regex with a canonical fence-aware, T###-scoped whole-file iterator, unifying checkbox semantics — with the tightening ratified consciously. Independent Test: On a fixture with T### rows, prose - [ ] lines, and fenced example checkboxes, the acceptance gate flags only genuine T###-scoped rows; a characterization test captures the old→new flagging; terminal-mission normalization is preserved. Prompt: /tasks/WP04-checkbox-parser-canon.md Requirement Refs: FR-008, FR-009, NFR-003
Included Subtasks
- ✅ T024 Add
iter_unchecked_subtask_rows(text) -> Iterator[str](whole-file, fence-aware, T###-scoped, yields offending line strings) onsrc/specify_cli/core/subtask_rows.pyshared constants - ✅ T025 [P] Characterization test: capture the acceptance gate's unchecked-row output before→after migration on a mixed fixture (T### rows + prose
[ ]+ fenced examples), ratifying the T###/fence/indent tightening (FR-009), intests/specify_cli/acceptance/test_find_unchecked_tasks_canon.py - ✅ T026 Migrate
src/specify_cli/acceptance/gates_core.py::_find_unchecked_tasksonto the new iterator - ✅ T027 [P] Test: terminal-mission normalization (all-WPs-terminal zeroes unchecked tasks) preserved after migration
- ✅ T028 Verify dead-code gate clean (stray regex removed);
ruff+mypyzero new findings
Dependencies
- None.
Risks & Mitigations
- Observable acceptance-gate change → T025 characterization ratifies it; T027 preserves the terminal normalization. Lowest value; keep tightly scoped.
Work Package WP05: Review-lock liveness fold (Priority: P2)
Goal: Fold review/lock.is_stale onto the canonical core/process_liveness.is_process_alive (return not is_process_alive(pid)), removing the last stray liveness probe. Independent Test: is_stale is branch-equivalent (live / dead / permission-denied) to the prior os.kill(pid,0) implementation; os.getpid() at acquire is retained. Prompt: /tasks/WP05-review-lock-liveness-fold.md Requirement Refs: FR-010, NFR-001
Included Subtasks
- □ T029 Fold
src/specify_cli/review/lock.py::is_staletoreturn not is_process_alive(self.pid); remove the livenessos.kill(pid,0); keepos.getpid()at acquire - □ T030 [P] Characterization test: 3-branch equivalence (live / dead / permission-denied) in
tests/specify_cli/review/test_lock_liveness_fold.py - □ T031 Verify no orphaned imports (dead-code gate);
ruff+mypyzero new findings
Dependencies
- Depends on WP02 (consumes the
is_process_alive(pid)->boolsignature; sequence after it settles). Equivalence-only — no PID-reuse hardening for the lock.
Risks & Mitigations
- Preserve branch-equivalence → T030 pins all three branches. Keep
os.getpid()intact.