tiny-spec-plan
Decides how the requirements get built, slices that into the tasks that build it,
and — just as important — hardens the constitution (constitution.md) that every
task will be implemented and reviewed against.
It writes two files in one pass: PLAN.md, the stable design narrative, and
tasks.md, the flat checklist tiny-spec-build executes and ticks. They are two files
for a good reason — tiny-spec-build rewrites tasks.md constantly, and mixing mutable
execution state into design prose would be worse — but they are one staleness unit.
This skill always writes and reconciles both together, and it is the only skill that
sets tasks.md's status:.
Artifacts live under .spec/: the shared constitution at the root
(.spec/constitution.md), the per-ticket SPEC.md/PLAN.md/tasks.md under
.spec/<slug>/. Resolve the active ticket dir from the current git branch: the
.spec/<slug>/ whose slug matches the branch name (one branch per ticket). If none
matches, use the sole ticket dir if there's exactly one; else ask which. Both skeletons
are inline below — write them from there, no file to read. Requires
.spec/<active>/SPEC.md.
Two cases pre-empt that order — ask instead of applying it: more than one dir
matches the branch (there is no defined tie-break, and inventing one here would
silently disagree with every other skill), or ticket dirs exist while you are on
main/master with no name match (the usual cause is a forgotten git switch, and
the sole-dir fallback would otherwise swallow it). Detached HEAD or no git repo is a
degraded case, not an ask case — branch match is simply unavailable, so fall
through to sole-dir and ask as written.
Step 1 — harden the constitution (constitution.md)
Do this first. The constitution is the spine of the whole flow and project-wide
— it lives at .spec/constitution.md (the root, shared across every ticket), and is
injected whole into every executor and reviewer. Make it strong and specific to this
project, not generic boilerplate. Fill in / sharpen all seven fixed sections:
- Style · 2. Engineering standards · 3. Guiding invariants ·
- Glossary · 5. Layout · 6. Definition of Done ·
- Verification commands.
Two sections carry the most weight — get them right:
- Guiding invariants — the non-negotiables a reviewer can fail a task on. Be concrete ("all timestamps are UTC ISO-8601", "no network calls in unit tests"), not aspirational ("write clean code").
- Verification commands — the exact, runnable gate (install → lint → test → build → run). The reviewer executes these literally, so they must actually work from a clean checkout. If setup is needed (e.g. install the package first), say so explicitly.
If the project came in through tiny-spec-adopt, the constitution was derived from
the codebase and its sections are marked inferred or declared. Hardening means
confirming the inferred ones against the plan you are about to write — an inferred gate
that has never been run is the single most dangerous thing in the file.
If there is a ## Design system section, it belongs to tiny-spec-design; harden
its values, don't restructure it:
- Every token needs a concrete value. "A consistent spacing scale" fails nothing.
space.1=4px …space.8=32px fails apadding: 19px. A token with no value is worse than no token, because it looks like a contract and isn't one. - Check the
SPEC.mdD<n>entries resolve. Every token a screen names must exist here. An entry namingcolor.accent.primarywhen the system defines no such token is a broken anchor — route it back totiny-spec-design, don't leave it dangling. - Confirm the
visual:command actually runs. It is the gate for every task carryingdesign:; an aspirational command means the visual gate silently never fires. If it doesn't work from a clean checkout, fix it or drop thedesign:refs.
Step 2 — write PLAN.md
Write .spec/<active>/PLAN.md with the structure below, filling it in:
---
status: current
updated: <ISO date>
---
# Plan — <project / feature name>
## Approach
<The design narrative: how the requirements will be met. Key decisions, the shape
of the solution, notable trade-offs. Enough that someone could derive the tasks
from it. Optional `### Phase` headings are fine for readability — they do NOT
parallelize or gate anything.>
<!-- optional: omit if N/A -->
## Architecture
<The moving parts and how they fit: components, data flow, key interfaces or
modules. A small diagram or bullet map is fine. Skip for changes too small to need it.>
## Requirement coverage
<Every REQ-N maps to where it's addressed. No requirement left unaddressed.>
- REQ-1 — <where/how addressed>
- REQ-2 — <where/how addressed>
- REQ-3 — <where/how addressed>
<!-- optional: omit if N/A -->
## Risks & mitigations
<What could go wrong (technical risk, unknowns, fragile areas) and how the plan
de-risks it.>
<!-- optional: omit if N/A -->
## Test strategy
<How the work will be verified beyond the constitution's gate — what to test, at
what level, and any fixtures/data needed.>
<!-- optional: omit if N/A -->
## Open questions
<Design questions still unresolved. A question that blocks the task list must be
answered here or routed back to tiny-spec-create before you slice.>
## Approach(required) — the design narrative: the shape of the solution, key decisions, trade-offs. Detailed enough to derive a task list from.## Requirement coverage(required) — map everyREQ-Nto where it's addressed. A requirement with no home is a gap: fix the approach or route back totiny-spec-create.- Optional sections (
Architecture,Risks & mitigations,Test strategy,Open questions) where they add value — omit any that don't apply.
Keep it proportional: a small change is a few paragraphs, not a phased epic.
An unresolved ## Open questions entry that blocks the slice is a stop. Don't guess
past it into a task list — say what's unresolved and route back to tiny-spec-create.
Step 3 — slice the approach into tasks
Walk the ## Approach you just wrote and break it into tasks. No waves, no
parallelism, no owns: contracts — tasks run one at a time, top to bottom. Each task is:
- Small and independently checkable — one slice a single executor can finish and a reviewer can grade in one pass. If you can't write a one-line acceptance for it, it's too big — split it.
- Ordered so each builds on the last. Tasks run sequentially, so a later task may freely assume an earlier task's code already exists. Put foundational work (types, schema, scaffolding) first. Order by dependency, not by guesswork.
- Right-sized, not fragmented. Don't split a cohesive change into five files' worth of micro-tasks just to look granular. Earned ceremony: fewer, meaningful tasks beat many trivial ones.
For each task, write:
- [ ] T<n> — <imperative description>
- acceptance: <one user-observable outcome that proves it's done>
- type: feat # optional; Conventional Commit type (defaults to feat)
- req: REQ-n # optional; the REQ-N this task delivers
- design: D-n # optional; the SPEC.md D<n> screen this task builds — arms the visual gate
- pause: <why> # optional; halt the build before this task so a human looks first
- files: <comma-separated hint of files it will touch>
The acceptance is what the reviewer checks against — make it observable
("spec --version prints the version and exits 0"), not internal ("version logic
added"). type picks the Conventional Commit type tiny-spec-build uses for this
task's code commit (feat | fix | docs | refactor | test | chore | build | ci | perf | style);
set it when the task is clearly not a feature, otherwise omit and it defaults to feat.
req ties the task to the requirement it satisfies (traceability). The files line
is a hint to focus the executor and reviewer; it is not enforced, so approximate paths
are fine.
design is the explicit opt-in to the visual gate — the one field that changes
how a task is graded. Set it when the task builds a surface described by a D<n> in
SPEC.md; the reviewer then renders that surface, measures the selectors the entry
names against the constitution's Design system tokens, and can fail the task on a
numeric deviation or a missing state. Omit it and the task is graded exactly as any
other. Two rules:
- Set it only on tasks that actually build the visible surface — not on the API call or the state store behind it. The gate is per-task, so this is how you keep the blast radius where you want it.
- Only set it if the constitution has a
visual:verification command. Without one the reviewer cannot render anything and will raise a blocker instead of a verdict.
pause is the other field that changes what happens rather than what gets written —
but where design: changes how a task is graded, pause: changes whether the build
continues. Set it and tiny-spec-build halts before that task, leaving it
unchecked, so a human reviews the approach while redirecting it is still cheap. The
value is one line saying what to look at.
Propose it only on work that is genuinely irreversible or wide-blast-radius:
- a data migration, or anything that writes to real rows;
- a destructive or bulk file operation;
- pulling in a new third-party dependency;
- an auth, permissions, or trust boundary;
- a public API or schema contract other people's code depends on.
When in doubt, leave it out. A pause the user didn't want is worse than no pause at all — it trains them to wave past the ones they did want, which is exactly the reflex that makes the mechanism useless the one time it matters. Most task lists should carry zero or one.
A caller may hand you a standing pause policy — "halt before anything that touches
auth", "stop before any schema migration" (tiny-spec-run passes one through in a
build-through run). Apply it to this task list: any task matching the description
gets a pause: naming the policy that put it there, on top of whatever you'd have set
anyway. A policy that matches nothing here is not an error — say so and move on, rather
than stretching a task to fit it.
Cover every part of the approach — together the tasks must deliver all REQ-N.
Don't leave a requirement with no task. Likewise, if SPEC.md has a ## Design
section, every D<n> in it needs at least one task carrying that design: reference —
a screen nobody is graded against is a screen that will be built wrong.
Step 4 — write tasks.md
Write .spec/<active>/tasks.md with the structure below — all tasks [ ] unchecked,
frontmatter status: current, updated: <today>:
---
status: current
updated: <ISO date>
---
# Tasks — <project / feature name>
> Executed top to bottom, one at a time. A checked `[x]` task is implemented AND
> reviewed. `type:`, `req:`, `design:`, and `pause:` are optional; `files:` is a hint,
> not an ownership contract. A task with `design:` is also graded against that screen's
> `D<n>` entry and the constitution's Design system. A task with `pause:` halts the
> build before it runs, so a human looks first.
## Tasks
- [ ] T1 — <one small, independently-checkable slice of work>
- acceptance: <one user-observable outcome that proves T1 is done>
- type: feat # optional; Conventional Commit type for this task's commit (defaults to feat)
- req: REQ-1 # optional; the REQ-N this task delivers
- files: <path, path>
- [ ] T2 — <next slice; assume T1's code exists>
- acceptance: <observable outcome>
- type: feat
- req: REQ-2
- design: D1 # optional; only on tasks that build the visible surface
- files: <path, path>
- [ ] T3 — <…>
- acceptance: <observable outcome>
- pause: <optional; what to check before this runs — irreversible work only>
- files: <path, path>
Update mode (SPEC changed → PLAN and tasks are stale)
When PLAN.md or tasks.md is status: stale, reconcile both in one pass — they
are one staleness unit, so never leave the run with them disagreeing:
Read the latest
.spec/<active>/decisions.mdchange entry to see what moved.Reconcile
.spec/<active>/PLAN.mdand the shared.spec/constitution.md— adjust only what the change requires; preserve the rest.Reconcile
.spec/<active>/tasks.md— add/alter/remove tasks to match the new approach, preserving existingT<n>ids where the task still exists; new tasks get the next free id.Completed-work guardrail: if a change touches a task already
[x], uncheck it ([ ]) and record the unchecked ids in.spec/<active>/decisions.mdfor human review, using the fixed skeleton. Never assume built work survived a change.## D-NNN — <short title> - type: change - date: <ISO date> - affects: T<n>, T<m> - note: <which tasks were unchecked and why>Set both
PLAN.mdandtasks.mdtostatus: currentand bumpupdated.
When done
Confirm the constitution is hardened and every REQ-N is covered, then report the task
count — and any pause: points you set, with their reason, so the user can drop one
before it fires.
Then point them at tiny-spec-build (one task at a time, reviewing as it goes), or
tiny-spec-run to drive it through.