spec-kitty / spec-kitty/spec-kitty

Decide: mission reopen after --discard must clear discarded_at, or be refused (dashboard-invisible reopened mission)

Open
#4,316 0 comments 0 reactions 0 assignees View on GitHub
bug priority:P2
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.