Imported from N0K0/claude-plugins-backalley (
plugins/process/skills/spec-issue/SKILL.md). Install upstream withnpx skills add N0K0/claude-plugins-backalley --skill spec-issue. Copyright stays with the author.
Spec Issue
Core principle: One spec, one source of truth. The GitHub Issue body if we're using the issues workflow; otherwise a local markdown file at docs/specs/<slug>.md.
Built-in plan sync: this skill is exempt — it does not modify issue checklists. See
../_shared/builtin-plan-sync.md.
Entry Gate
Before doing any work, detect the mode (GH issues vs local markdown), then run the mode-specific gate.
Mode detection
- If the user said "brainstorm issue N" (numeric), that's GH mode.
- If
.issues/exists at the repo root and the gh plugin'sdetect_repotool is available, default to GH mode. - If
docs/specs/<slug>.mdalready exists for the topic, that's local mode. - Otherwise, call
detect_repoto probe. If the tool is missing or returns an error, use local mode. - If both paths remain viable (gh is available AND no existing file disambiguates), ask once: "Write this spec to a GitHub issue or a local file at
docs/specs/<slug>.md?" Default to GH.
Announce the chosen mode at the start of the run, e.g. "I'm using the spec-issue skill in local mode to spec out <slug>."
GH mode gate
- Call
issue_pullwith the.issues/directory path to sync all issues locally. - Existing issue (issue number provided): read
.issues/issue-{N}.mdand check labels. If theneeds-speclabel is NOT present, stop with: "Issue #{N} doesn't have theneeds-speclabel. [If has-spec: Run plan-issue instead. If in-progress: Run execute-issue instead.]" - New issue: create a new issue file at
.issues/issue-new.mdwith a placeholder title, theneeds-speclabel, and an empty body. Do NOT push yet.
Local mode gate
-
If
docs/specs/<slug>.mdexists, read it and check its frontmatterstatus:field. Ifstatusis notneeds-spec(missing, or alreadyhas-spec/in-progress), stop with: "docs/specs/<slug>.mdhas status<X>. [If has-spec: Run plan-issue. If in-progress: Run execute-issue.]" -
If the file doesn't exist, create it with this frontmatter and an empty body:
--- title: <placeholder — fill during Q&A> status: needs-spec ---
Do not proceed past the entry gate unless all checks pass.
The Process
-
Survey repo design docs. Probe the repo root for a conventional design-doc directory, taking the first hit from this fixed list and stopping:
docs/specs/docs/design/docs/rfcs/spec/specs/
If none exist, skip this step entirely — do NOT create a directory. If one exists, list its
*.mdfiles and read the first#heading plus any frontmattertags:/status:from each. Hold this list in memory ONLY for the conflict scan at step 7. It does NOT feed into approach generation or Q&A framing — keeping it scoped to conflict detection prevents drift into "design-doc indexing." If a second conventional folder also exists, note it once to the user ("Also founddocs/design/— only surveyingdocs/specs/.") and proceed with the first. -
Read any existing context (issue body or local file body) for prior requirements or discussion. Use this as input — don't re-ask things already answered.
-
Ask clarifying questions one at a time using the
AskUserQuestiontool. Follow these three conventions whenever calling the tool (also enforced in the Common Mistakes and Red Flags sections below):- Prose framing first. Output the question statement as user-facing prose immediately before the tool call, so the user sees the question in narrative flow before the UI prompt appears.
- Aim for four meaningful choices, one recommended. Provide up to four distinct
options(the tool's maximum). Put the recommended choice first with"(Recommended)"appended to itslabel. Use fewer than four only when additional options would be invented filler. - Cap each
descriptionat two sentences. Edit longer descriptions down rather than letting them sprawl.
Wait for an answer before asking the next question. Focus on:
- Purpose: what problem does this solve?
- Constraints: what must it work with or within?
- Success criteria: how do we know it's done?
- Scope: what's explicitly out of scope?
-
Only one question per message. Don't bundle.
-
Once you understand the problem, propose 2-3 approaches with trade-offs. Lead with your recommended approach and explain why it's the best fit. Be concrete — name the approach, describe how it works, and call out the key trade-off.
-
Section-by-section approval loop. After the user picks an approach, execute this loop literally — do NOT batch sections:
a. Identify N sections (typically: Problem statement, Approach, Acceptance criteria, Edge cases, Scope boundaries, Dependencies). Fix N before the loop starts and announce it to the user (e.g., "I'll present this in 6 sections."). b. For each section i in order, from 1 to N: i. Write section i as prose in the assistant's message (NOT in the issue body yet). ii. Call
AskUserQuestionwith questionApprove section i of N: <section name>?. Options depend on whether the section is required or optional: - Required sections (Problem statement, Acceptance criteria, Edge cases, Scope boundaries, Dependencies — the five enforced by the Internal Review Loop's checklist gate): options are "Approve (Recommended)" and "Revise — I have changes". ASkipoption is NOT offered; required sections cannot be skipped. - Optional sections (Approach comparison, Implementation notes, anything beyond the required five): options are "Approve (Recommended)", "Revise — I have changes", "Skip this section". iii. IfApprove: append section i's text to the in-progress spec buffer. iv. IfRevise: collect the user's changes, regenerate section i, and repeat (ii) for the same i. v. IfSkip(optional sections only): record a one-line stub> Section "<name>" intentionally skipped during spec.in the buffer and continue. c. After the loop, the assembled buffer is what gets written to the issue body / local file in step 7. Do not write to disk during the loop. -
If the user mentions this issue is part of a larger initiative, include a
Parent: #N(GH mode) orParent: docs/specs/<other>.md(local mode) line at the top of the spec body when writing it. -
Once all sections are approved, assemble the full spec and write it into:
- GH mode: the local issue file body (do NOT push yet).
- Local mode: the body of
docs/specs/<slug>.md, below the frontmatter.
Conflict scan. Immediately after writing the assembled body but before running the Internal Review Loop, walk the design-doc list from step 0 (if any). Precision target: no false-negative budget; false positives are expected and acceptable. For each surveyed doc ask: "Does the new spec contradict, supersede, or duplicate this doc?" — flag liberally; under-flagging is the failure mode. If any conflict is found, append a
## Conflicts with existing design docssection to the spec body listing each conflict as a bullet:## Conflicts with existing design docs - `docs/specs/2026-04-15-auth-model.md` — proposes session tokens; this spec proposes JWT. **Resolution deferred to execution.**The conflict section is informational. This skill MUST NOT attempt to resolve conflicts during spec writing —
execute-issueowns resolution because spec-time the implementer has less context than execute-time. If no conflicts are found, omit the section entirely. -
Run the internal review loop before presenting the spec to the user (see below).
-
User approval gate. Before any status transition or push, hand the assembled spec back to the user for explicit approval.
a. Present the spec as a single message. Form is mechanical, not judgmental:
- If the assembled body is ≤ 120 lines: paste the full body inline, preceded by the one-line preface
Full spec below (under 120 lines):. - If the assembled body is > 120 lines: paste a recap — the first paragraph of the Problem statement plus the list of section headings — followed by the absolute file path. In GH mode, also include the GitHub URL if the issue's
url:frontmatter is populated; if it is not (typical for a brand-new issue that hasn't been pushed yet), omit the URL line — the push at step 11 happens after this gate. Preface the recap withSpec is N lines — showing recap; full file at <path>.. b. CallAskUserQuestionwith the question "Approve this spec as written?" and these options in this order: - "Approve as written (Recommended)" — proceed to follow-up items and status transition.
- "Revise — I'll point out what to change" — wait for the user's edits, apply them, re-run the Internal Review Loop, and re-ask this gate.
- "Discard — start over" — drop the body, keep the issue/file at
needs-spec, and stop. c. The model MUST NOT callissue_push(GH mode) or updatestatus:(local mode) before this gate returns "Approve as written." d. If section-by-section approval (step 5) was performed in full and every section was explicitly approved, this gate still runs — its role is "approve the assembled artifact," distinct from "approve each section." The framing may be shortened to "Same content you approved section-by-section, now assembled — confirm?" but the gate still runs.
- If the assembled body is ≤ 120 lines: paste the full body inline, preceded by the one-line preface
-
Create follow-up items for deferred decisions (see Follow-Up Items below).
-
Persist the spec:
- GH mode: call
issue_pushwith the.issues/directory to sync the spec (and any follow-up issues) to GitHub. This is the only push in the entire flow. Ifissue_pushfails, the local file under.issues/remains the source of truth — surface the error verbatim and do NOT transition status. - Local mode: the file is already written. If the write earlier failed because the parent directory was missing, retry once after
mkdir -pon the parent; if it still fails, stop with the error surfaced verbatim — do not silently fall back to writing elsewhere.
- GH mode: call
-
Transition status:
- GH mode: call
issue_updateto remove theneeds-speclabel and addhas-spec. - Local mode: update the frontmatter
status: needs-spec→status: has-spec.
- GH mode: call
-
Tell the user where the spec lives. Emit a structured block with absolute paths so the terminal can click them:
-
GH mode:
Spec written to issue #{N}. File: /abs/path/.issues/issue-{N}-{slug}.md URL: https://github.com/{owner}/{repo}/issues/{N} Next: run `plan-issue {N}` to create the implementation checklist.The URL comes from the issue file's
url:frontmatter (set byissue_push). If it is missing, omit the URL line and addURL unavailable — issue may not have been pushed yet.. -
Local mode:
Spec written. File: /abs/path/docs/specs/{slug}.md Next: run `plan-issue docs/specs/{slug}.md` to create the implementation checklist.If
issue_pushfailed in step 11 (GH mode), emit the file path only and addPush to GitHub failed — local file at <path> is the current source of truth.so the user can recover.
-
Acceptance Criteria Format
When writing the Acceptance criteria section (in the step 5 section-by-section
loop and whenever the Internal Review Loop touches it), give each criterion its own
### subsection: a heading stating the testable condition, then one or two sentences
of rationale — why it matters and how "done" is judged. This keeps each criterion's
context complete on its own. The shape is shared with plan-issue's checklist and
documented in ../_shared/checklist-format.md.
Criteria are conditions, not execution tasks, so they do not get - [ ]
checkboxes — that space belongs to plan-issue's Implementation Checklist, and
execute-issue / finish-issue parse it.
## Acceptance criteria
### Spec writes to exactly one place
Splitting a spec across an issue body and a local file creates two sources of
truth that silently diverge. Verified by checking only one destination is written.
### Every required section is present
A spec missing edge cases or scope boundaries sends ambiguity downstream to
plan-issue. Verified by the Internal Review Loop checklist gate.
Internal Review Loop
After writing the spec to its destination (step 7) but before the User approval gate (step 9), run this loop (max 5 iterations):
a. Checklist gate (fast): Verify the spec contains all 5 required sections. If any is missing or empty, add it to the spec.
- Problem statement — what problem are we solving and for whom?
- Acceptance criteria — concrete, testable conditions for "done", each written as its own
###subsection (heading = the condition, prose = why it matters / how it's verified) per the Acceptance Criteria Format above - Edge cases — what could go wrong, what are the boundary conditions?
- Scope boundaries — what is explicitly out of scope?
- Dependencies — what does this depend on, what depends on this?
b. Subagent review (deep): Dispatch a review subagent with these instructions: "Review the following spec for completeness, internal consistency, and clarity. Flag: vague acceptance criteria, contradictions between sections, unstated assumptions, missing error handling, scope creep beyond the stated problem. Return a list of specific issues found, or 'PASS' if the spec is ready." Pass the full spec text to the subagent.
c. If the subagent returns issues: Fix each issue in the spec, increment the iteration counter, and go back to step (a).
d. If the subagent returns PASS or iteration count reaches 5: Proceed to step 9 (User approval gate). If stopped at 5 iterations, tell the user at the approval gate: "Internal review found issues I couldn't fully resolve after 5 attempts. Please pay extra attention to the flagged areas before approving."
Follow-Up Items
During the Q&A phase, the user often picks an approach but signals they may revisit the decision later. Watch for language like:
- "Let's start with X and see if Y is needed"
- "Go with X for now, we can switch to Y later"
- "X first, then benchmark/evaluate Y"
- Picking a partial option like "expose API now, integrate UI later"
- Any answer that scopes work down while acknowledging the remainder is still valuable
These are deferred decisions — the user chose a path but explicitly left the door open for an alternative.
After the User approval gate (step 9) returns "Approve as written," but before persisting (step 11):
-
Review the Q&A history for deferred decisions. Collect each one: what was chosen, what was deferred, and the trigger condition for revisiting.
-
If any deferred decisions were found, present them to the user as prose first (Rule 1), then call
AskUserQuestion(multiSelect: true) so the user can pick which follow-ups to create. Strive for up to four meaningful options — typically a "Create all" option plus one option per deferred decision (Rule 2). Because this is a multi-select question, omit"(Recommended)"from the labels per the multi-select edge case in this issue's spec. Keep each option'sdescriptionto two sentences or fewer (Rule 3). -
For each approved follow-up, create:
- GH mode: an issue file at
.issues/issue-new-{slug}.mdwithneeds-speclabel, a brief context body, andParent: #N. Pushed alongside the spec in step 11. - Local mode: a new file at
docs/specs/<slug>.mdwith frontmatterstatus: needs-spec, a brief context body, andParent: docs/specs/<parent>.md.
- GH mode: an issue file at
If no deferred decisions were found, skip this section silently.
Handling Feedback (GH mode only)
When the user says "check for feedback", "there's feedback on GitHub", or similar (this section does not apply to local mode):
- Get the timestamp from the local issue file's
pulled_atfrontmatter field. - Call
issue_comments_listwithissue_numberandsinceset to that timestamp. - If no new comments, tell the user: "No new comments on issue #N since the last update."
- If new comments are found, for each comment: summarize what the commenter is asking for, then apply the feedback to the spec in the local issue file.
- Re-run the internal review loop on the updated spec, then re-run the User approval gate (step 9) before pushing.
- Call
issue_pushwith the.issues/directory. - Present a summary of changes made.
Key Principles
- One question at a time — don't overwhelm the user with a list of questions
- Multiple choice preferred — easier to answer than open-ended, speeds up the process
- YAGNI ruthlessly — if a feature isn't clearly needed, cut it from the spec
- Explore alternatives — always propose 2-3 approaches before committing to one
- Section-by-section approval — don't dump the whole design at once; get buy-in as you go
- Final assembled-spec approval is mandatory — the section-by-section pass approves parts; the gate at step 9 approves the whole artifact, and both must happen
- Spec lives in exactly one place — the issue body OR the local file, never split
- Conflicts are surfaced, not resolved — note them in the spec body so
execute-issuecan handle them with full context
Common Mistakes
Problem: Skipping mode detection and defaulting to GH when gh isn't installed.
Fix: Always run mode detection first. If detect_repo fails, use local mode.
Problem: Asking multiple questions at once. Fix: One question per message. Wait for the answer. Then ask the next.
Problem: Writing multiple sections in a single assistant turn.
Fix: One section, one AskUserQuestion, then the next section. The section loop at step 5 is literal — do not batch.
Problem: Skipping the approach comparison. Fix: Always propose 2-3 approaches before committing.
Problem: Running the Internal Review Loop and immediately transitioning status without asking the user.
Fix: Always run the User approval gate at step 9 before issue_push (GH) or any status: update (local).
Problem: Telling the user "spec written to issue #N" with no path and no URL.
Fix: Emit the structured File: / URL: / Next: block from step 13 every time.
Problem: Silently resolving a conflict with an existing design doc by adjusting the new spec to avoid contradiction.
Fix: Record the conflict in the ## Conflicts with existing design docs section at step 7 and defer resolution to execute-issue.
Problem: Auto-mode is on, so the assistant skips the section-by-section or final approval gates. Fix: Auto-mode is not a license to skip the approval gates. They are skill-mandated, not optional clarifications — the runtime queues the question if the user is away.
Problem: Forgetting the status transition.
Fix: GH mode — swap needs-spec → has-spec via issue_update. Local mode — update frontmatter status:.
Problem: Presenting the spec to the user without running internal review. Fix: Always run the checklist gate + subagent review before the User approval gate.
Problem: Calling AskUserQuestion without surfacing the question in prose first.
Fix: Output the question statement as user-facing prose immediately before the tool call (Rule 1).
Problem: Offering only two or three options when four meaningful choices exist.
Fix: Aim for four distinct options, recommended first with "(Recommended)" in its label (Rule 2). Fewer is acceptable only when more would be invented filler.
Problem: Letting choice description text sprawl past two sentences.
Fix: Cap each description at two sentences (Rule 3). Edit longer descriptions down rather than leaving them long.
Red Flags
Never:
- Skip user approval on any section of the design
- Call
issue_push(GH) or setstatus: has-spec(local) before the User approval gate at step 9 returns "Approve as written" - Skip the spec-folder survey at step 0 when one of the conventional directories exists
- Proceed without the
needs-specstatus being present (label in GH mode, frontmatter in local mode) - Ask more than one question per message
- Call
AskUserQuestionwithout first emitting the question statement as prose - Let a choice
descriptionexceed two sentences - Start implementation — this skill ends when the spec is written
- Write the spec in two places (e.g., both an issue body and a local file)
Always:
- Detect mode at the entry gate and announce which mode is active
- Run step 0's survey before the Q&A
- Ask one question at a time
- Emit the question as prose immediately before each
AskUserQuestioncall - Aim for four meaningful
options, recommended first with"(Recommended)" - Run the section-by-section loop at step 5 literally
- Run internal review (checklist gate + subagent) before the User approval gate
- Run the User approval gate at step 9 before any push or status change
- Append a
## Conflicts with existing design docssection when step 7's conflict scan finds any - Emit the structured
File: / URL: / Next:block at step 13 - Transition status after the spec is written and approved
- Tell the user what to run next (
plan-issue)
Integration
Requires (GH mode only): gh plugin (detect_repo, issue_pull, issue_push, issue_update, issue_comments_list). Local mode has no external dependencies.
Next skill: plan-issue (creates the implementation checklist from the spec)
Status transition: needs-spec → has-spec (label in GH mode, frontmatter field in local mode)