spec-kitty / spec-kitty/spec-kitty
Decide: mission reopen after --discard must clear discarded_at, or be refused (dashboard-invisible reopened mission)
- Dominant language
- Python
- Stars
- 1.6k
- Forks
- 165
- Avg merge
- 14h 22m
- Merged PRs (30d)
- 343
Description
## Summary (BLUF)
After PR #4257 (#704) lands, `spec-kitty mission close --discard` stamps a durable `discarded_at` marker on `meta.json`, and `_derive_mission_status` checks it **first** — so any mission still carrying the marker reads `"discarded"` regardless of live lane state. `spec-kitty mission reopen` clears `merged_*` markers but **never clears `discarded_at`**. A mission reopened after a discard would therefore stay invisible on the dashboard even though it is live again. This needs a lifecycle-semantics decision, not just a code tweak — so it was deliberately **not folded** into #4257.
## The interaction
- `reopen_cmd` (`src/specify_cli/cli/commands/mission_type.py`) calls only `clear_merge_metadata(...)`; `discarded_at` survives.
- `_derive_mission_status` (`src/specify_cli/dashboard/scanner.py`) returns `"discarded"` whenever `discarded_at` is set, before any lane-count branch.
- The dashboard filters `mission_status == "discarded"` out of the list endpoint → a reopened-after-discard mission is hidden.
## Why it is narrow (not a release blocker)
Reopen of a discarded mission is doubly-gated and practically unreachable today:
1. `is_mission_completed(feature_dir)` — a discarded mission's WPs are typically non-terminal, so reopen is refused.
2. `_branch_resolvable(...)` — discard deletes the mission branch locally, so reopen is refused unless the branch survives on a remote.
The window where both gates pass is narrow but not impossible.
## The decision to make
Two coherent directions — pick one and record it:
- **(A) Reopen is allowed for discarded missions →** `reopen_cmd` must also pop `discarded_at` (mirror `clear_merge_metadata`, or add a `clear_discard_metadata` primitive), so the mission is no longer contradictory (live but stamped discarded). Add a discard→reopen test.
- **(B) Reopen of a discarded mission is unsupported →** add an explicit fail-closed guard in `reopen_cmd` ("cannot reopen a discarded mission; discard is terminal"), so the state is impossible-by-construction rather than merely improbable.
Discard is an *abandonment* terminal, distinct from *completed/merged* (which is what reopen was designed for, #1926), which is why this is a product call rather than an obvious mechanical fix.
## Provenance
Surfaced by the architect-alphonso review lens on the PR #4257 landing pass (MINOR 2). Related: #704, #1926 (reopen completion precondition).
Contributor guide
Research direction
Read src/specify_cli/cli/commands/mission_type.py and src/specify_cli/dashboard/scanner.py, then inspect the existing completion and branch-resolvability checks used by reopen. Confirm with maintainers whether discard is terminal or reopen should clear discarded_at, and add the corresponding discard-to-reopen test so the chosen lifecycle policy is enforced.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- cli, developer-experience
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100