tiny-spec-create
Captures what the user wants and why, as testable REQ-N requirements.
This is the front door of the per-ticket flow. A spec can be bound to a ticket or worked
ad-hoc — both are first-class.
The suite works one spec at a time, namespaced per spec. Artifacts live under
.spec/ in the project root (the user's cwd) — never in this skill's directory. Two
are project-wide and shared at the .spec/ root (constitution.md, memory.md);
the per-spec ones (SPEC.md, PLAN.md, tasks.md, decisions.md) live under
.spec/<slug>/. The SPEC.md and constitution.md skeletons are inline below — write
them from there, no files to read.
Pick your mode first — before doing anything else
Decide which mode you are in before you ask a single question, create a directory, or touch git — each mode forbids side effects the others require, so getting here late means the damage is already done.
Check in this order; the first match wins:
| # | If | Mode | Go to |
|---|---|---|---|
| 1 | .spec/ exists but .spec/constitution.md does not |
reseed | Reseed a missing constitution — repair only, then stop |
| 2 | .spec/ does not exist |
fresh (first run) | Seeded mode if BREAKDOWN.md exists, else Pick the slug → First run — scaffold → Write SPEC.md |
| 3 | the active spec's SPEC.md already exists |
update | Update mode — edit in place, never overwrite |
| 4 | otherwise (new spec in an existing .spec/) |
fresh (new spec) | Seeded mode if BREAKDOWN.md exists, else Pick the slug → Write SPEC.md |
Rule 1 outranks everything, including Seeded mode — a missing constitution is a repair, and repairs don't create work items. Rule 3 outranks rule 4 so that a re-run on existing work can never overwrite it.
To evaluate rules 3 and 4 you need the active spec dir. Resolve it read-only first (see Pick the slug for the shared resolution order). Only create a dir once you know you're in a fresh mode.
If the user (or tiny-spec-run) says this is new work, skip the sole-dir fallback
and go to rule 4. That fallback exists to find the ticket you're already on; letting it
match when someone asked for a new spec quietly retargets the run onto an old ticket
and marks its downstream artifacts stale for no reason.
Reseed a missing constitution
Check this before anything else. The constitution is project-wide, so it can go
missing — deleted, or never committed — while a perfectly good .spec/<slug>/ sits next
to it. Then it is unrecoverable: the scaffold below never fires, tiny-spec-plan only
hardens an existing one, and tiny-spec-build would inject nothing.
If this repo already contains real source code, hand off to tiny-spec-adopt — it
derives the constitution from the code itself, which beats anything reconstructible from
here. Say so and stop; that skill also handles the staleness propagation below.
Otherwise (little or no code yet), do only this:
- Resolve the active spec dir read-only — you may need its
SPEC.md. There may be none; that's fine. - Write
.spec/constitution.mdusing the skeleton in First run — scaffold below, filled in from whatever already exists, in this order of preference: aBREAKDOWN.md## Decisionsblock at the project root if there is one (Stack + Code-lives → Style and Layout; verification hints → Verification commands; cross-cutting → Guiding invariants), then the activeSPEC.md, then the codebase itself. A declared source beats inference. Ask the user only what you genuinely cannot infer. - Flag downstream work as unverified — across every spec, not just the active one.
The constitution is project-wide, so any
[x]task in any.spec/*/was built and reviewed against a document that did not exist. For each spec dir whosetasks.mdhas checked tasks, set itstatus: staleand log adecisions.mdentry in that spec (type: change) saying the constitution was reseeded and the completed tasks were never checked against it. - Report what you seeded, say plainly that it was inferred and needs review (it
gets injected whole into every executor and reviewer, so a wrong Verification
commands block silently corrupts every future gate), note that
SPEC.md/PLAN.mdand the branch were left untouched, and point the user attiny-spec-planto harden it. Then stop.
Do not run the interview, create a spec dir, switch branches, or touch SPEC.md.
This is a repair, not a new spec.
Seeded mode (BREAKDOWN.md present)
Fresh modes only — reseed (rule 1) and update (rule 3) both outrank this.
Before interviewing, check for BREAKDOWN.md at the project root (written by
tiny-spec-scope). If it exists and the user is creating one of its stories, seed from
it instead of running the full interview — confirm, don't re-ask:
- Pick the story. Ask which story (or infer from the user's request); match it to
its
## Feature:→Story:entry by slug or title. - Slug + binding. Use the story's slug for
.spec/<slug>/. Take the ticket provider from the Decisions Platform and the id from the story's tracker parent or the user (ask for the id if the placeholder is still blank; omit theticket:block entirely if the platform is ad-hoc). - Requirements. Promote the story's
AC:lines intoREQ-N— verbatim where already atomic; split any that hide two capabilities behind an "and". - Constitution (first run only). Seed
constitution.mdfrom the## Decisionsblock instead of interviewing stack/layout: Stack + Code-lives → Style and Layout; any verification hints → Verification commands; cross-cutting concerns → Guiding invariants. Ifconstitution.mdalready exists, reuse it. - Designs. If the story carries
design:paths, note them and point the user attiny-spec-design— that skill reads the files and writes this spec'sD<n>entries against a token system. Don't describe the screens here. - Confirm the captured
REQ-Nwith the user, then writeSPEC.mdas below.
Only the project-wide questions collapse — still confirm this story's binding and
requirements. If there is no BREAKDOWN.md, or no entry matches, run the full
interview below unchanged.
Exception — a story run. If the caller says this is a story run and names the
story's slug (tiny-spec-run does, in build-through mode), skip step 6's confirmation
and write SPEC.md straight out. The AC: lines are the approval: the user reviewed
them when they wrote or accepted BREAKDOWN.md, and re-confirming them once per story is
asking the same question twice — across a seven-story run it is the difference between
walking away and being interrupted seven times. Report the captured REQ-N instead of
asking about them.
This waives only that confirmation. Everything that is a genuine question still stops
the run: a story whose AC: lines contradict each other or the Decisions block, an AC:
you cannot turn into a testable REQ-N, a missing ticket id the platform needs, a design
export the story names that isn't on disk. Those are not ceremony — they are the human
input the run is supposed to stop for. Never guess past one because a run is in progress.
Pick the slug (resolve the active dir)
Fresh modes only — in reseed or update mode the active dir already exists and you resolve it read-only, as above. Do not run this section.
If a spec dir already resolves for the current branch, reuse it — don't invent a new
slug. Asking for a slug when .spec/PROJ-123/ already matches the branch is how you
end up with both PROJ-123/ and dark-mode/ matching feature/PROJ-123-dark-mode,
which makes the ticket permanently ambiguous for every downstream skill.
Each spec lives in its own directory .spec/<slug>/. At the start of the interview,
establish the slug. There are two paths — both are first-class:
- Bound to a ticket. Ask for the provider (
jira | github | ado | monday), id, url, and optionally the current status. Derive the slug from the platform key: verbatim when filesystem-safe (PROJ-123); otherwise normalize — GitHub#42→gh-42, Monday item→monday-<id>, ADOAB#77→ado-77. - Ad-hoc (no ticket). Perfectly supported — just confirm there's no ticket and use a
short kebab-case slug of the feature name (e.g.
dark-mode,perf-pass). The SPEC omits theticket:frontmatter block, and commits drop theRefs:footer (everything else — namespacing, the constitution, the build loop — is identical). You can bind a ticket later by adding the block toSPEC.md.
Create .spec/<slug>/.
One branch per spec — how the active spec is resolved. Work lives on a branch named
after the slug (e.g. PROJ-123 or feature/PROJ-123-dark-mode); downstream skills
resolve the active spec from the branch name, so several can be in flight on separate
branches at once. If the user isn't already on such a branch, create one (e.g.
git switch -c <slug>) so the new spec resolves by branch match.
First run — scaffold
If .spec/ does not exist:
If this repo already contains real source code, run tiny-spec-adopt first. It
derives the constitution from the actual lint/test/build commands, layout, and
conventions — an interviewed constitution on an existing codebase is how you get a gate
that names commands the project doesn't have. Come back here for the spec itself.
Otherwise:
Create
.spec/and the active spec dir.spec/<slug>/(on the spec's branch — see above).Short interview (keep it short — earned ceremony):
- the ticket binding (above);
- the intent in one paragraph;
- the language/stack and where code lives;
- the must-have requirements (the capabilities, not the design).
Seed the shared constitution at
.spec/constitution.md(the root, not the spec dir — it is project-wide), filling in what the interview or aBREAKDOWN.md## Decisionsblock already told you (Style, Layout, Verification commands at minimum). Leave the rest fortiny-spec-planto harden — but never leave a section empty of intent. Ifconstitution.mdalready exists (a prior spec created it), reuse it — do not overwrite the project's constitution. Write it with this structure (drop the indentation when you write the file):# Constitution > This is the strongest, most persistent document in the project. It is > **project-wide** — it lives at the `.spec/` root and anchors *every* spec, not > any one of them. Every task is implemented and reviewed against it. Keep it true; > keep it lean. Project-specific richness belongs here — not scattered across tasks. ## Style <Formatting, naming, language idioms. The defaults a reader should assume.> ## Engineering standards <Error handling, logging, testing approach, dependency policy, what "tested" means here.> ## Guiding invariants <The non-negotiables. "Never X." "Always Y." The rules a reviewer can fail a task on.> ## Glossary <Domain term — one-line definition. Keep the team speaking one language.> ## Layout <Where things live. Directory map. Where new code of each kind goes.> ## Definition of Done <The bar a task must clear to be checked off: e.g. code + tests + docs updated, gate green, no TODOs left, matches the invariants above.> ## Verification commands <The exact gate. The reviewer runs these. Omit any stage this project doesn't have. - install: `...` - lint: `...` - test: `...` - build: `...` - run: `...` >A
## Design systemsection istiny-spec-design's — leave it absent here. A dead design heading on a CLI or library is worse than none: it gets injected into every executor and reviewer prompt for the life of the project.
Write SPEC.md
Write .spec/<slug>/SPEC.md with the structure below, filling it in:
---
status: current
updated: <ISO date>
# Ticket binding (reference-only). Omit this whole block if there is no ticket.
ticket:
provider: jira | github | ado | monday
id: <PROJ-123 | #42 | AB#77 | item id>
url: <link to the ticket>
status: <optional manual mirror of the platform status, e.g. In Progress>
---
# <Project / feature name>
<!-- optional: omit if N/A -->
## Context
<Why now — the background, the problem, what prompted this. No solution detail.>
## Intent
<One paragraph: what this is and why it exists. The "what" and "why", never the "how".>
## Requirements
<Each REQ is one user-observable, testable capability. No implementation detail.
If a line hides two capabilities behind an "and", split it.>
- REQ-1 — <capability>
- REQ-2 — <capability>
- REQ-3 — <capability>
<!-- optional: omit if N/A -->
## Non-goals
<What this explicitly does NOT cover — scope boundaries that prevent creep.>
<!-- optional: omit if N/A -->
## Success criteria
<How we'll know the whole spec succeeded, beyond the per-requirement acceptance —
e.g. a metric, an end-to-end scenario, a stakeholder sign-off.>
<!-- optional: omit if N/A -->
## Open questions
<Unresolved questions that may change requirements. Resolve before/while planning.>
<!-- optional: omit if N/A -->
## Links
<Ticket, related specs, design docs, prior art.>
- the ticket binding frontmatter block (or omit it if there's no ticket);
- a one-paragraph intent;
- a
## Requirementslist — eachREQ-Na single user-observable, testable capability with no implementation detail ("the CLI accepts a--jsonflag and prints valid JSON", not "add a json module"); - the optional sections (
Context,Non-goals,Success criteria,Open questions,Links) where they add value — omit any that don't apply.
Number requirements REQ-1, REQ-2, …. Keep each atomic — if a line has an "and" that
hides two capabilities, split it.
If this spec has a visual surface, run tiny-spec-design afterwards. It adds a
## Design section — one D<n> per screen, written against a project-wide token system
— and arms the visual gate. Don't write screen descriptions here; a description not
anchored to tokens is how per-screen drift starts.
Update mode (re-run on an existing spec)
When the active spec's SPEC.md already exists and the user wants a change to
requirements (resolve the active dir by branch match):
Edit
.spec/<active>/SPEC.mdin place — add/alter/removeREQ-N, preserving existing ids where the requirement still exists.Flip downstream stale: set
PLAN.mdandtasks.mdfrontmatter tostatus: stale(if they exist).Log it: append a
decisions.mdentry to.spec/<active>/decisions.md, using the fixed skeleton (type: change, the affectedREQ-N). Create the file if absent:## D-NNN — <short title> - type: change - date: <ISO date> - affects: REQ-N - note: <what changed + why>Completed-work guardrail. If a changed
REQ-Nis delivered by a task already[x]intasks.md(follow itsreq:field), uncheck it and name the unchecked ids in the samedecisions.mdentry.tiny-spec-buildresumes from the checkbox state, so a task left[x]is a task it will never revisit — never assume built work survived a requirement change.Set
SPEC.mdstatus: currentand bumpupdated. Do this even if it was alreadycurrent.SPEC.mdis the root of the chain and no other skill in the suite writes that flag, so a hand-editedstaleSPEC is otherwise unclearable and the chain jams at the root.
If the spec has a ## Design section and the change touches a screen, hand off to
tiny-spec-design — it owns those entries and their staleness hashes.
Tell the user which downstream docs went stale and to re-run tiny-spec-plan to
reconcile.
New spec? To start a different piece of work, re-run this skill on a new branch — it creates a new
.spec/<slug>/that resolves by branch match. The sharedconstitution.mdandmemory.mdcarry over; the previous spec's artifacts stay untouched on disk.
When done
Report the requirements captured and point the user at tiny-spec-plan (or
tiny-spec-design first, if this spec has screens to anchor).