Close
Retire one SpecScore artifact when its work is done. close is a thin driver
over the specscore <kind> change-status CLI verbs: it resolves the artifact's
kind, confirms the terminal status with the user, captures a reason when one is
required, and performs exactly one change-status call. That single call
does the status rewrite, writes any ## Resolution note (--note), relocates a
seed to archived/, and syncs the index — atomically, with rollback on failure.
Load-bearing invariant: close NEVER hand-edits status. Every transition
goes through the CLI verb — not as a primary path, and not as a fallback on
failure. A hand-edit anywhere is a contract violation.
Implements the Close Skill Feature.
When to Use
- A single Idea, Feature, or sidekick seed has reached end-of-life (shipped,
superseded, rejected, or parked) and the user wants to retire it.
- The user types
/close <artifact> or asks to retire/close/reject an artifact.
Refuse and redirect when:
- The invocation does not resolve to exactly one existing artifact, or resolves
ambiguously to more than one kind → stop and ask the user which artifact;
write nothing. (AC:
resolves-kind-and-verb)
- The transition would be illegal for the resolved artifact (the CLI returns
exit
4) → surface the current status and the legal source set; do not
retry, do not hand-edit. (AC: surfaces-illegal-transition)
Kind → verb → terminal statuses
close resolves the artifact's kind by location, which selects the verb and
the candidate terminal statuses:
| Kind |
Resolved at |
Verb |
Candidate terminals |
| Idea |
spec/ideas/<slug>.md |
specscore idea change-status |
Implemented, Rejected, Stale |
| Feature |
spec/features/<id>/README.md |
specscore feature change-status |
Deprecated |
| sidekick seed |
spec/ideas/seeds/<slug>.md |
specscore sidekick change-status |
Implemented, Rejected, Stale |
Archival is orthogonal to status — it is not a terminal status. An artifact is closed to one of the terminals above and retains that status when filed away; filing it out of active view is a separate archive action, never a change-status --to=Archived call.
(Resolving the kind is a filesystem check — not a CLI call. close does not
query the CLI and then call it again.)
The close flow
Resolve the artifact and kind (REQ resolve-artifact-kind). Take the
slug / feature-id / path argument and resolve it by location to exactly one
of the three kinds above. If it resolves to nothing, or ambiguously to more
than one kind, STOP and ask the user — never guess.
Present the candidate terminals and confirm (REQ
confirm-terminal-before-close). Closing is terminal, so present the
candidate terminal status(es) for the resolved kind and obtain an explicit
user choice. Never auto-select a terminal status.
Capture the reason (REQ reason-for-negative-transitions).
- For a negative transition (an Idea or seed
Rejected), a reason is
mandatory. Collect it up front and pass it via --note. Do not invoke
the verb without it — the CLI also enforces this with exit 2, but
collecting it first avoids a wasted round-trip.
- For a passive-decay transition (an Idea or seed
Stale — nobody decided
against it, it simply was never carried forward), OFFER to record an
optional --note (e.g. why it lost relevance).
- For a positive/neutral transition (
Implemented / Deprecated), OFFER
to record an optional --note (the rationale or where the work shipped).
- When
close is invoked by another skill (non-interactive), the reason
MUST be supplied by the caller as an explicit argument. close MUST NOT
fabricate or auto-generate a reason for a reason-required transition; if the
caller passed none, refuse and surface the requirement. (AC:
ai-caller-must-pass-reason)
Drive the verb — one call (REQ cli-only-transition). Invoke exactly one
specscore <kind> change-status <id> --to=<status> [--note <markdown>]. That
single call atomically performs the status rewrite, the ## Resolution note
write, any seed relocation + type: tag, and the spec lint --fix index
sync, with rollback on any failure. Do not issue a second mutating call,
a follow-up edit, or a manual index fix-up.
Single-call guardrail. If a future need ever makes one call
insufficient to close atomically (multi-artifact, cross-repo, or a
transition the CLI can't express as one verb), that is the signal to add a
dedicated specscore close command in the CLI — not to multi-call from
this skill.
Branch on the exit code (REQ branch-on-cli-exit) — and NEVER fall back
to a hand-edit on any non-zero:
| Exit |
Meaning |
Action |
0 |
success |
Surface the verb's <id>: <from> → <to> line. Done. (AC: closes-via-cli-on-success) |
1 |
archive collision |
Surface the conflict; stop. |
2 |
invalid args / missing required reason |
Collect the reason and retry, or correct --to. |
3 |
artifact not found |
Surface; stop. |
4 |
illegal transition |
Surface the current status + legal source set; stop. |
10 |
rollback applied (lint/IO) |
Surface the error; stop. |
127 |
CLI missing |
/specscore:install, then retry. |
8 |
CLI too old |
upgrade, then retry. |
Kind availability (REQ seed-close-requires-verb)
Closing a seed depends on the cli/sidekick/change-status verb (shipped in
specscore-cli ≥ v0.12.0). If the installed specscore lacks it (exit 127 /
8 on the sidekick change-status call), surface install/upgrade guidance —
do NOT hand-edit the seed as a workaround. Idea and Feature closes are
unaffected (their change-status verbs predate this).
Not Doing
- Bulk close. One artifact per invocation. Closing several in one call
(e.g. a batch of seeds) is out of scope for the MVP — it is N invocations.
- Reopen / un-close.
close only moves an artifact to a terminal status;
reversing a close is not its job.
- Deploy, verify, recap, or plan side effects.
close records a terminal
status and nothing else.
Verification
Red Flags
- Editing the
**Status:** line, frontmatter status:/type:, or an index row directly — ever, including as a fallback
- Issuing more than one mutating CLI call for a single close (see the single-call guardrail)
- Auto-selecting a terminal status instead of confirming with the user
- Invoking the verb for a reason-required transition without a reason
- Fabricating a reason for an AI-driven (non-interactive) close
- Retrying after exit
4 (illegal transition) instead of surfacing and stopping
- Hand-editing a seed as a workaround when the
sidekick change-status verb is missing
1---2name: close3description: Retires a single SpecScore artifact — an Idea, Feature, or sidekick seed — by driving the appropriate `specscore <kind> change-status` CLI verb with an optional/required `--note`, never by hand-editing. One artifact, one change-status call: the status transition, the `## Resolution` note, any relocation, and the index sync all happen atomically in that single verb. Negative transitions (e.g. a seed `Rejected`) require a reason. Close confirms the terminal status, drives the verb, and branches on its exit code — it never edits the `**Status:**` line, frontmatter, `type:`, or an index row, even on CLI failure. Trigger: "close", "/close", "specstudio:close", "retire this artifact".4---56# Close78Retire one SpecScore artifact when its work is done. `close` is a thin driver9over the `specscore <kind> change-status` CLI verbs: it resolves the artifact's10kind, confirms the terminal status with the user, captures a reason when one is11required, and performs **exactly one** `change-status` call. That single call12does the status rewrite, writes any `## Resolution` note (`--note`), relocates a13seed to `archived/`, and syncs the index — atomically, with rollback on failure.1415**Load-bearing invariant:** `close` NEVER hand-edits status. Every transition16goes through the CLI verb — not as a primary path, and not as a fallback on17failure. A hand-edit anywhere is a contract violation.1819Implements the [Close Skill Feature](../../spec/features/skills/close/README.md).2021## When to Use2223- A single Idea, Feature, or sidekick seed has reached end-of-life (shipped,24 superseded, rejected, or parked) and the user wants to retire it.25- The user types `/close <artifact>` or asks to retire/close/reject an artifact.2627**Refuse and redirect when:**2829- The invocation does not resolve to exactly one existing artifact, or resolves30 ambiguously to more than one kind → stop and ask the user which artifact;31 write nothing. (AC: `resolves-kind-and-verb`)32- The transition would be illegal for the resolved artifact (the CLI returns33 exit `4`) → surface the current status and the legal source set; do not34 retry, do not hand-edit. (AC: `surfaces-illegal-transition`)3536## Kind → verb → terminal statuses3738`close` resolves the artifact's **kind by location**, which selects the verb and39the candidate terminal statuses:4041| Kind | Resolved at | Verb | Candidate terminals |42|---|---|---|---|43| Idea | `spec/ideas/<slug>.md` | `specscore idea change-status` | `Implemented`, `Rejected`, `Stale` |44| Feature | `spec/features/<id>/README.md` | `specscore feature change-status` | `Deprecated` |45| sidekick seed | `spec/ideas/seeds/<slug>.md` | `specscore sidekick change-status` | `Implemented`, `Rejected`, `Stale` |4647Archival is **orthogonal to status** — it is not a terminal status. An artifact is closed to one of the terminals above and *retains* that status when filed away; filing it out of active view is a separate archive action, never a `change-status --to=Archived` call.4849(Resolving the kind is a filesystem check — **not** a CLI call. `close` does not50query the CLI and then call it again.)5152## The close flow53541. **Resolve the artifact and kind** (REQ `resolve-artifact-kind`). Take the55 slug / feature-id / path argument and resolve it by location to exactly one56 of the three kinds above. If it resolves to nothing, or ambiguously to more57 than one kind, STOP and ask the user — never guess.58592. **Present the candidate terminals and confirm** (REQ60 `confirm-terminal-before-close`). Closing is terminal, so present the61 candidate terminal status(es) for the resolved kind and obtain an **explicit**62 user choice. Never auto-select a terminal status.63643. **Capture the reason** (REQ `reason-for-negative-transitions`).65 - For a **negative** transition (an Idea or seed `Rejected`), a reason is66 **mandatory**. Collect it up front and pass it via `--note`. Do not invoke67 the verb without it — the CLI also enforces this with exit `2`, but68 collecting it first avoids a wasted round-trip.69 - For a **passive-decay** transition (an Idea or seed `Stale` — nobody decided70 against it, it simply was never carried forward), OFFER to record an71 optional `--note` (e.g. why it lost relevance).72 - For a **positive/neutral** transition (`Implemented` / `Deprecated`), OFFER73 to record an optional `--note` (the rationale or where the work shipped).74 - **When `close` is invoked by another skill** (non-interactive), the reason75 MUST be supplied by the caller as an explicit argument. `close` MUST NOT76 fabricate or auto-generate a reason for a reason-required transition; if the77 caller passed none, refuse and surface the requirement. (AC:78 `ai-caller-must-pass-reason`)79804. **Drive the verb — one call** (REQ `cli-only-transition`). Invoke exactly one81 `specscore <kind> change-status <id> --to=<status> [--note <markdown>]`. That82 single call atomically performs the status rewrite, the `## Resolution` note83 write, any seed relocation + `type:` tag, and the `spec lint --fix` index84 sync, with rollback on any failure. Do **not** issue a second mutating call,85 a follow-up `edit`, or a manual index fix-up.8687 > **Single-call guardrail.** If a future need ever makes one call88 > insufficient to close atomically (multi-artifact, cross-repo, or a89 > transition the CLI can't express as one verb), that is the signal to add a90 > dedicated `specscore close` command in the CLI — **not** to multi-call from91 > this skill.92935. **Branch on the exit code** (REQ `branch-on-cli-exit`) — and NEVER fall back94 to a hand-edit on any non-zero:9596 | Exit | Meaning | Action |97 |---|---|---|98 | `0` | success | Surface the verb's `<id>: <from> → <to>` line. Done. (AC: `closes-via-cli-on-success`) |99 | `1` | archive collision | Surface the conflict; stop. |100 | `2` | invalid args / missing required reason | Collect the reason and retry, or correct `--to`. |101 | `3` | artifact not found | Surface; stop. |102 | `4` | illegal transition | Surface the current status + legal source set; stop. |103 | `10` | rollback applied (lint/IO) | Surface the error; stop. |104 | `127` | CLI missing | `/specscore:install`, then retry. |105 | `8` | CLI too old | upgrade, then retry. |106107## Kind availability (REQ `seed-close-requires-verb`)108109Closing a **seed** depends on the `cli/sidekick/change-status` verb (shipped in110`specscore-cli` ≥ v0.12.0). If the installed `specscore` lacks it (exit `127` /111`8` on the `sidekick change-status` call), surface install/upgrade guidance —112do NOT hand-edit the seed as a workaround. Idea and Feature closes are113unaffected (their `change-status` verbs predate this).114115## Not Doing116117- **Bulk close.** One artifact per invocation. Closing several in one call118 (e.g. a batch of seeds) is out of scope for the MVP — it is N invocations.119- **Reopen / un-close.** `close` only moves an artifact to a terminal status;120 reversing a close is not its job.121- **Deploy, verify, recap, or plan side effects.** `close` records a terminal122 status and nothing else.123124## Verification125126- [ ] Resolved the argument to exactly one artifact + kind; ambiguity stopped and asked127- [ ] Presented candidate terminals and got an explicit user choice (no auto-select)128- [ ] Negative transition collected a reason; AI-caller closes required a passed-in reason (no fabrication)129- [ ] Performed exactly one `specscore <kind> change-status` call (no second mutating call, no hand-edit)130- [ ] Branched on the verb's exit code; never hand-edited on non-zero131- [ ] Seed close surfaced install/upgrade guidance when the verb was absent132133## Red Flags134135- Editing the `**Status:**` line, frontmatter `status:`/`type:`, or an index row directly — ever, including as a fallback136- Issuing more than one mutating CLI call for a single close (see the single-call guardrail)137- Auto-selecting a terminal status instead of confirming with the user138- Invoking the verb for a reason-required transition without a reason139- Fabricating a reason for an AI-driven (non-interactive) close140- Retrying after exit `4` (illegal transition) instead of surfacing and stopping141- Hand-editing a seed as a workaround when the `sidekick change-status` verb is missing