issue-spec
Create GitHub issues as lean-spec style specifications for human-AI aligned implementation on whatever repo this skill is installed in. GitHub issues are the sole spec medium — no spec files.
This skill is the repo-agnostic methodology half of the contract. Each consumer repo overlays its own delta in two places:
- Repo CLAUDE.md — names the GitHub slug (
codervisor/lean-spec,onsager-ai/onsager,onsager-ai/duhem, …) and any repo-specific spec principles (e.g. Onsager's Reach + seam rule, lean-spec's provider-agnostic core + i18n, Duhem's worked-example + schema-impact). <repo>-dev-processsister skill — carries the area-label taxonomy, the spec-vs-trivialgate, the SDD loop wiring, and the repo's pre-push check gate + CI-failure patterns.- Global
pre-push/pr-lifecycleskills — the shared pre-push and post-push methodology (installed fromonsager-ai/dev-skills). The consumer repo overlays only its gate command, collision patterns, and anypr-spec-syncautomation via CLAUDE.md /<repo>-dev-process; the skills themselves are not per-repo.
When in doubt about the target repo, run git remote -v and read the repo's CLAUDE.md and its *-dev-process skill before drafting the spec body.
Why GitHub Issues, Not Files
lean-spec uses Markdown files with YAML frontmatter for metadata. We replace that entirely with GitHub issues because:
- Status → Issue state (open/closed)
- Priority → Labels (
priority:critical,priority:high,priority:medium,priority:low) - Tags → Labels (
area:<subsystem>,feat,fix,refactor,perf) - Dependencies → Issue references (
depends on #42) and sub-issues - Parent/Child → Sub-issues via
mcp__github__sub_issue_write - Transitions → Issue timeline (automatic, auditable)
- Collaboration → Comments, reactions, assignments, mentions
GitHub gives us versioned metadata, collaboration, and relationship tracking for free. No CLI needed, no frontmatter to manage, no sync problems.
Philosophy
Three principles from lean-spec, universal across consumer repos:
- Context Economy — Keep issue body under ~2000 tokens. Larger features split into parent + child issues. Small specs produce better AI output and better human review.
- Intent Over Implementation — Document the why and what, not the how. Implementation details belong in PRs, not spec issues. The spec captures human intent that isn't in the code.
- Living Documents — Specs evolve via issue comments and edits. The issue's open/closed state tracks lifecycle; the comment thread becomes the decision record.
Each consumer repo may add 1–2 repo-specific principles on top — read the repo's CLAUDE.md and its *-dev-process sister skill before drafting. Examples of overlay principles you'll find in the wild:
- Reach ships with the primitive (Onsager) — new user-facing primitives must scope in nav entry, first-run flow, empty-state CTAs, and auth gating; deferring discoverability is the bad call.
- Provider-agnostic core + i18n (lean-spec) — changes to the provider abstraction declare
## Provider impact; user-visible strings land in bothenandzh-CN. - Worked example + schema impact (Duhem) — product-surface specs ship with a minimal Verification Definition that exercises the surface; schema-touching changes declare
## Schema impact.
If the overlay introduces an additional issue-body section or label (e.g. Provider impact, Schema impact, provider-impact, schema-impact, i18n), apply it. The repo's CLAUDE.md or sister skill is authoritative for which extras it requires.
When to use this skill
Use when:
- A change touches multiple files or subsystems.
- Multiple stakeholders need alignment before implementation.
- The AI needs explicit boundaries for a non-trivial feature.
- Work will span multiple PRs (parent + child specs).
- The change touches an externally observable contract — schema, CLI surface, public API, provider trait, event manifest — regardless of diff size. Consumer repos may name additional always-spec surfaces in their CLAUDE.md or sister skill; honour them.
Skip when:
- A typo or doc-only fix. Use the
triviallabel on the PR instead. - A one-line bug fix with an obvious reproduction. Just open a PR with
Fixes #existing. - The feature already has a spec issue — extend that spec, don't create another.
Default is spec, not trivial. If invocation of this skill is itself the decision — the user said "spec this" or the change clearly isn't a typo/one-liner — proceed straight to Discover. Do not stop to confirm spec-vs-trivial. The "Skip when" list is a self-veto for unambiguously trivial diffs; everything else is a spec by default. trivial is a sparingly-used escape hatch (see the repo's *-dev-process sister skill and the global pre-push skill), not a 50/50 fork to ask about.
Setup
| Parameter | Default | Example override |
|---|---|---|
| Topic | (required) | "session timeout", "fix heartbeat race" |
| Scope | Inferred from codebase | "only stiglab", "only the cli" |
| Priority | medium |
critical, high, low |
| Labels | Auto from type + area | "spec, feat, area:<sub>" |
| Parent | None | #42 (umbrella issue) |
If the user says "spec session timeout", start immediately. Do not ask clarifying questions unless the topic is genuinely ambiguous — and do not ask whether the change should be a trivial-labeled PR instead. Invocation of this skill is the decision; the "Skip when" list above is the only place that question gets re-litigated, and only for unambiguously trivial diffs.
Workflow
1. Discover Search existing issues and codebase
2. Design Draft the spec issue body
3. Align Partition human decisions vs AI work
4. Validate Self-check before creating
5. Publish Create GitHub issue (+ sub-issues if splitting)
1. Discover
Before writing anything, understand what exists:
- Search existing GitHub issues on the target repo for related or duplicate specs.
- Grep the codebase for types, functions, modules related to the topic.
- Read key files that will be affected.
- Check git log for recent changes in the area.
- Read the repo's
CLAUDE.mdand any canonical product doc it points at (e.g. an architecture doc, an ADR, adocs/<product>-spec.md) — they ground the spec in existing commitments and identify always-spec surfaces.
If a related spec issue already exists, reference it — don't duplicate.
2. Design
Read references/spec-format.md for the section-by-section format guide.
Don't hard-wrap prose, list items, or blockquote lines. GitHub renders issue and comment bodies with breaks: true — every newline inside a paragraph, list item, or blockquote becomes a <br>, producing visible mid-sentence breaks. Source files in many repos wrap at ~70–100 columns; issue bodies must not. Each paragraph, list item, and blockquote line is a single long line; only blank lines separate paragraphs, and each new bullet/quote line starts on its own line. Fenced code blocks and tables preserve formatting and are unaffected. Headings are single-line by markdown's own rules.
Draft the issue body using the lean-spec structure:
## Overview
Problem statement and motivation. Why does this matter?
## Design
Technical approach: data flow, API changes, architecture decisions.
Keep it high-level — intent, not implementation.
## Plan
- [ ] Checklist of concrete deliverables
- [ ] Each item independently verifiable
- [ ] Order reflects implementation sequence
## Test
- [ ] How to verify each plan item
- [ ] Include: unit tests, integration tests, manual checks
## Notes
Tradeoffs, context, references. Optional — omit if empty.
Consumer repos may require additional sections (e.g. ## Provider impact, ## Schema impact, ## Worked example) — read the repo's CLAUDE.md / sister skill for the full list and the rules for when each section is required vs. optional. Templates under templates/ cover the universal shape; consumer-repo overlays may extend the template.
Context economy check: If the issue body exceeds ~2000 tokens, split it:
- Create a parent issue with Overview + high-level Plan
- Create child issues (sub-issues), one per independent concern
- Each child has its own Design, Plan, Test sections
- Link children to parent via
mcp__github__sub_issue_write
3. Align
Add an Alignment section to the issue body (this extends lean-spec for human-AI collaboration):
## Alignment
### Human decides
- [ ] Only items that cross a named boundary — say which one
### AI implements
- [ ] Everything else, including reversible technical calls — state the call, don't ask
### Open questions
> Items that block AI implementation until a human decides
Rules:
- The default is "AI implements". An item goes under "Human decides" only when you can name the specific boundary it crosses. If you cannot name one, it is not a human decision — put it under "AI implements", state the call in the line itself, and proceed.
- The boundary is the operator's, not this skill's. Different operators authorize differently: one delegates merges, another reviews everything, another gates on spend. This skill does not define where the line sits — it requires that a line be named before work is routed across it. Consult whatever the repo or the operator declares (a rules file, a mandate roster, a CONTRIBUTING note); if nothing is declared, ask once for the boundary rather than assuming a conservative one.
- Escalating is free for the author and expensive for the human, so the pressure runs one way. "Human decides" is the exceptional branch and should read like one — a spec whose Alignment section is mostly human items is a spec that has not been thought through.
- If an item requires both a decision and execution, split it — but only if the decision half names a boundary. Otherwise it is one AI item.
- Open questions use
>blockquotes so they're visually distinct - Reconciliation has an owner: whoever asked the question. When a human answers — in an issue comment, a PR review, or a chat — update the Alignment section in the same sitting. An answered question left unchecked makes the backlog lie about what is blocked, and downstream tooling that routes by issue state cannot see comments.
4. Validate
Before creating the issue, self-check:
- Body is under ~2000 tokens (context economy)
- Prose paragraphs, list items, and blockquote lines are not hard-wrapped (each is one long line; blank line between paragraphs, new bullet/quote line on its own)
- Overview explains why, not just what
- Design captures intent, not implementation details
- Plan items are concrete and independently verifiable
- Test items map to Plan items
- Any repo-specific required section (e.g.
## Provider impact,## Schema impact) is present, or the spec provably doesn't touch that surface - Human/AI boundaries are explicit — no "figure it out" items
- Every "Human decides" item names the boundary it crosses; anything that cannot is an "AI implements" item with the call stated
- No duplicate of an existing issue
- Dependencies are referenced by issue number
5. Publish
Create the issue using mcp__github__issue_write against the consumer repo:
Title format: spec(<area>): <short description>
<area> is drawn from the consumer repo's area-label taxonomy — read the *-dev-process sister skill (or the repo's CLAUDE.md if the sister skill defers) for the canonical list. Examples observed across consumer repos: spec(stiglab): add session timeout, spec(provider): github provider — issue CRUD via MCP, spec(schema): add api/observe action type.
Labels: Apply via the issue creation:
spec— always, marks this as a spec issue- Type:
feat,fix,refactor,perf - Area (consumer-repo taxonomy)
- Priority:
priority:critical,priority:high,priority:medium,priority:low - Any consumer-repo cross-cutting labels (e.g.
provider-impact,schema-impact,i18n) when the corresponding overlay section is non-empty
Sub-issues: If this is a child of a parent spec, link it using mcp__github__sub_issue_write.
After creating, report to the user:
- Issue number and URL
- Token count estimate (flag if over 2000)
- Any open questions that need human decisions
- Sub-issue links if the spec was split
Status Lifecycle
GitHub issue state alone tracks lifecycle:
open → closed
- open: Spec exists, work may be in flight. Open questions, if any, are resolved in the issue thread.
- closed: All plan items done, tests passing. PR merge with
Closes #Ncloses it automatically.
No status labels are applied — the issue's open/closed state plus the comment thread is the audit trail. PRs cross-link via Closes #N / Part of #N in the PR body; the global pr-lifecycle skill covers how Plan checkboxes get ticked as Part of PRs land.
Since "in flight" isn't a label anymore, the scripts/ helpers below reconstruct that view on demand — they read the open state plus linked PRs so you can see which open specs are actually being worked.
Spec Relationships via Sub-Issues
Use GitHub sub-issues for parent/child decomposition:
| Relationship | GitHub mechanism | When to use |
|---|---|---|
| Parent/Child | Sub-issues (mcp__github__sub_issue_write) |
Large feature decomposed into pieces |
| Depends On | Issue body reference (depends on #N) |
Spec blocked until another finishes |
| Related | Issue body reference (related: #N) |
Loosely connected specs |
Decision rule: Remove the dependency — does the spec still make sense? If no → sub-issue (child). If yes but blocked → depends on.
Example decomposition (shape, not specific to any one repo):
spec(<area>): umbrella feature ← parent issue
├── spec(<area>): concern A ← sub-issue
├── spec(<area>): concern B ← sub-issue
└── spec(<other-area>): UI surface for A+B ← sub-issue
When a contract under a repo's architectural rule splits work across two subsystems (e.g. a back-end producer + a front-end consumer of an event), use the parent-plus-children shape: one parent spec captures the end-to-end slice, one child spec per subsystem captures its half. The contract lives in the parent's Plan; each child scopes to a single area label. Producer and consumer halves can land in separate PRs as long as both close before the parent does — but the parent's Plan should require a contract test that fails until both halves exist.
Guidance
- Small is better. A 500-token spec that captures intent clearly beats a 3000-token spec that tries to cover everything. Split into sub-issues early.
- Discover first. Always search existing issues before creating. Duplicate specs create confusion.
- One concern per issue. If a spec covers two independent changes, split into sub-issues with a shared parent.
- Reference code, not concepts. Point to actual types, functions, files — not abstract ideas. Use concrete paths like
crates/<sub>/src/...orpackages/<pkg>/src/...rather than "the foo module." - Open questions are alignment points. These are where AI must stop and ask a human. Make them explicit, specific, and include the impact of each decision.
- Comments are the decision record. When a human resolves an open question, they comment on the issue. The thread becomes the audit trail.
- Use specs for alignment, not for everything. Regular bugs and small tasks don't need specs. Use specs when: multiple stakeholders need alignment, intent needs persistence, or the AI needs clear boundaries.
Handoff to implementation
Once the spec is ready to implement:
- Create a branch referencing the issue:
claude/spec-<N>-<slug>(Claude-owned) or any name (human-owned). - Follow the SDD loop in the repo's
*-dev-processsister skill. - Pre-push via the global
pre-pushskill (spec-link check plus the repo's check gate, named in its CLAUDE.md /*-dev-process). - PR body must include
Closes #N(slice complete) orPart of #N(scaffolding). - On merge GitHub auto-closes
Closes #Nissues; the merger ticks Plan items onPart of #Nparents manually. Seepr-lifecycle.
Repo-agnostic by construction; consumer overlay is required
This skill is the methodology; it intentionally does not name a repo, an area taxonomy, or a set of cross-cutting labels. Every consumer repo overlays those via:
- Its
CLAUDE.md(repo-specific principles, target GitHub slug, always-spec surfaces). - Its
*-dev-processsister skill (area-label taxonomy, spec-vs-trivialgate, SDD loop, the repo's pre-push check gate + CI-failure patterns that the globalpre-push/pr-lifecycleskills read). - Optionally an additional product spec doc (e.g.
docs/<product>-spec.md, an architecture ADR set) that the repo's CLAUDE.md points at.
Treat reading those as part of step 1 (Discover). Don't draft a spec without having loaded the consumer repo's overlay context first — a spec that names the wrong area label, misses a required section, or proposes something the repo's seam / schema / provider rule forbids is a spec the human has to rewrite.
References
| Reference | When to read |
|---|---|
| references/spec-format.md | Always — section-by-section guide with worked examples |
Repo's CLAUDE.md |
Always — repo-specific principles + always-spec surfaces |
Repo's *-dev-process sister skill |
Always — area-label taxonomy + spec-vs-trivial gate |
pr-lifecycle (global) |
When publishing — post-push workflow; the repo's CLAUDE.md says whether it automates pr-spec-sync. |
Scripts
Because no status label is applied, these helpers reconstruct the lifecycle view from issue state + linked PRs so you don't have to open each issue. Both take --repo owner/name (defaults to the current repo) and need gh + jq.
| Script | Purpose |
|---|---|
| scripts/spec-list.sh | List spec issues with derived activity + area + plan progress. --state open|closed|all, --area <a>. |
scripts/spec-show.sh <N> |
Detail one spec: derived activity, every linked PR + its state, plan progress, outstanding open questions. |
Derived-activity legend: ○ todo (open, no PR) · ● in-progress (open, ≥1 PR open) · ◐ partial (open, only merged PRs — e.g. a Part of #N parent) · ✓ done (closed). The SOP lifecycle is still just open → closed; this is a reporting overlay, not new state.
Templates
| Template | Purpose |
|---|---|
| templates/issue-spec-template.md | Universal issue body template — copy and fill |
Consumer repos may ship an extended template alongside this one (e.g. with a ## Provider impact or ## Schema impact block pre-filled). Prefer the consumer-repo extended template when present.