Write technical specs that let agents implement autonomously. Use for "write a spec", "plan this feature", "create a planning doc".
71
87%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Follow writing-voice for prose sections. For ownership or flow explanations, lead with the useful model, ground it in the relevant boundary, and keep only the diagrams and prose implementation needs.
A specification gives an agent or maintainer the context they need to implement a feature autonomously. The goal is not to describe everything exhaustively. The goal is to show enough evidence that the direction is credible and give the implementer a concrete launch point.
A spec is in-flight scaffolding, not the durable record. It plans work and holds research while the work is underway. It is not authoritative and does not outlive the work. Durable decisions live in docs/adr/, shared vocabulary in docs/CONTEXT.md, current state in docs/reference/ and the code. When a load-bearing decision crystallizes while you are writing the spec, record it as a Proposed ADR in docs/adr/ right then and reference it from the spec; do not leave it buried in the spec to be "harvested" later. When the work lands, the ADR flips to Accepted and the spec is deleted (see spec-execution). Git and docs/spec-history.md keep the history.
Note: This guide uses
[PLACEHOLDER]markers for content you must fill in. Code blocks show templates; replace all bracketed content with your feature's details.
Load these on demand based on the spec's decision surface:
Specs should:
A good spec is a launching pad, not a script to follow.
Before outlining sections, apply the one-sentence-test. If you can't name what this spec is about in one concrete sentence, the design is not coherent yet. That is the finding, and the spec is not ready.
The first screen of a spec must answer:
What is this?
Is it active, implemented, superseded, or historical?
What is the current shape?
What is the target shape?
What proves the change is done?Large specs are allowed. Thoroughness is useful when the work is deep. The rule is not "split after N lines." The rule is: do not make one reader job fight another.
Split or add a short active slice when a document mixes:
When keeping everything in one file, add a "How to read this spec" block near the top:
Read first:
One Sentence
Current State
Target Shape
Implementation Plan
Verification
Read if changing the architecture:
Design Decisions
Rejected Alternatives
Edge Cases
Historical only:
Implementation Notes
Superseded Decisions
Execution PromptsThe reader should be able to get the current truth in one minute, the model in five minutes, and the execution path in fifteen minutes.
All implementation specs live in root /specs/. Do not create nested specs under apps/ or packages/.
Name new specs specs/YYYYMMDDThhmmss-feature-name.md: local timestamp, kebab-case feature name.
Prompt and handoff artifacts can live beside specs with explicit suffixes like .prompt.md, .handoff.md, or .execute.md. They should link back to the canonical spec and should not be treated as the current implementation plan unless the suffix says so.
Classify every material decision:
| Class | Resolved by | Rule |
|---|---|---|
| 1 | Evidence | Verify with source, test, or version check. |
| 2 | Design coherence | Apply the spec thesis consistently. |
| 3 | Taste under constraints | Pick deliberately and write the constraint. |
Before any "keep" decision, ask: "Would I add this if it did not already exist?"
Never let evidence questions hide behind design coherence. Verify before deleting old paths. For examples and failure modes, read references/decision-hygiene.md.
Not every spec needs every section. A small feature might skip Research Findings. A migration spec might focus heavily on Edge Cases. Use judgment.
# [Feature Name]
**Date**: [YYYY-MM-DD]
**Status**: Draft | In Progress
**Owner**: [Name/team responsible for decisions]
**Branch**: [optional: branch name if work has started]
**Supersedes**: [optional: previous spec paths]
**Superseded by**: [optional: later spec path]Every spec needs one concrete sentence before the overview. This is the maintainer's anchor.
## One Sentence
[One sentence naming the new shape and the boundary it changes.]One paragraph max. Describe what the feature does. Don't sell it.
## Overview
[One to two sentences describing what this feature adds or changes and what it enables. Be specific about the capability, not vague about benefits.]Structure as Current State, Problems, then Desired State.
## Motivation
### Current State
[Show actual code or configuration demonstrating how things work TODAY. Use real code blocks, not prose descriptions.]
This creates problems:
1. **[Problem Title]**: [Specific explanation of what breaks or is painful]
2. **[Problem Title]**: [Specific explanation of what breaks or is painful]
### Desired State
[Brief description of what the target looks like. Can include a code snippet showing the ideal API or structure.]This is where specs shine. Document what you FOUND, not what you assumed.
## Research Findings
### [Topic Researched]
[Description of what you investigated and methodology]
| [Category] | [Dimension 1] | [Dimension 2] |
| ------------- | -------------- | ---------------- |
| [Project/Lib] | [What they do] | [Their approach] |
| [Project/Lib] | [What they do] | [Their approach] |
**Key finding**: [Your main discovery, for example that no standard exists, or that everyone does X]
**Implication**: [What this means for your design decisions]Include:
Use a table for traceability. Every material decision should have a class and rationale.
A load-bearing decision (a Class 2 coherence or Class 3 taste choice about architecture, ownership, an API shape, or a rejected alternative worth not re-litigating) belongs in an ADR, not only in this table. Write it as a Proposed ADR in docs/adr/ as soon as it crystallizes and reference the ADR number in the table's rationale. The table then traces the spec's decisions to their durable home; the ADR survives after the spec is deleted.
## Design Decisions
| Decision | Class | Choice | Rationale |
| ------------------- | ----------- | ---------------- | ------------------------------- |
| [Decision point] | 1 evidence | [What you chose] | [Source, test, or version checked] |
| [Decision point] | 2 coherence | [What you chose] | [How this follows the thesis] |
| [Decision point] | 3 taste | [What you chose] | [Constraint and trade-off] |
| [Deferred decision] | Deferred | Deferred | [Why it is deferred and what would bring it back] |Diagrams over prose. Prefer fenced text diagrams, file trees, route tables, and before/after blocks. Use simple ASCII diagrams by default because they are fast to write and easy to edit. Use box-drawing characters only when a polished diagram is worth the extra weight.
## Architecture
[Describe what the diagram shows][caller]
-> [boundary or route]
-> [policy or validation]
-> [storage or primitive]For multi-step flows:
Step 1: [Step name]
[What happens in this step]
Step 2: [Step name]
[What happens in this step]When the spec introduces a coherent set of new primitives (column types, action variants, error kinds, modifier methods, etc.), present them as a catalog: a compact code block that lists every primitive with a one-line annotation, followed by detail sections only for the ones that need elaboration.
Catalogs let a reader scan the entire surface in one glance before diving into any single primitive.
## The field.* catalog
```ts
field.string<TBrand?>(s?) // TEXT (TBrand for branded strings)
field.number(s?) // REAL
field.integer(s?) // INTEGER
field.boolean() // INTEGER 0/1
field.select([value]) // TEXT (single-element set)
field.select([...]) // TEXT + CHECK constraint
field.json<S extends TSchema>(schema) // TEXT JSON-encoded, schema required
nullable(inner) // Type.Union([inner, Type.Null()]) (standalone)
field.datetime(s?) // RFC 3339 string, branded DateTimeString
field.string<IanaTimeZone>() // IANA zone string, branded IanaTimeZone
```
Every primitive justified by N+ existing call sites in the audit.
### What was considered and rejected
| Candidate | Why rejected |
|---|---|
| `field.id()` | Subsumed by `field.string<IdBrand>()` + a co-located `generate*` factory |
| `field.array(of)` | Subsumed by `field.json(Type.Array(of))` |
| Declaring `_v` as a column | Library-managed; positional in `defineTable(v1, v2, ...)` |A "rejected candidates" table is often as useful as the catalog itself: it shows the implementer what not to add, and why. The reader gains confidence that the surface is tight.
When the spec changes a consumer-facing API, show at least 2-3 real call sites translated, not invented examples. Real code surfaces semantic shifts the abstract proposal misses.
Find actual usages in the codebase first. Show the verbatim "Before" with file:line, then the "After" translation. Annotate any non-obvious mapping with a brief comment.
## Call sites: before and after
### honeycrisp notes table
**Before** (`apps/honeycrisp/src/lib/workspace.ts:78`):
```ts
const notesTable = defineTable(
type({ id: NoteId, title: 'string', _v: '1' }),
type({ id: NoteId, title: 'string', wordCount: 'number | undefined', _v: '2' }),
).migrate(...)
```
**After**:
```ts
const notesTable = defineTable(
{ id: field.string<NoteId>(), title: field.string() },
{
id: field.string<NoteId>(),
title: field.string(),
wordCount: nullable(field.number()),
},
).migrate(({ value, version }) => {
switch (version) {
case 1: return { ...value, wordCount: null };
case 2: return value;
}
});
```
**Semantic shift to flag**: rows previously stored with `wordCount` key absent will now read as `null` instead of `undefined`. Affects app code doing `if (row.wordCount === undefined)`. Also: `_v` no longer appears in the column record; it is library-managed and stripped from returned rows.The "semantic shift to flag" callouts are critical: they're what the implementer needs to grep for and codemod across the codebase.
Break into phases. Use checkboxes for tracking. Phase 1 should be detailed; later phases can be rougher (the implementer will flesh them out).
## Implementation Plan
### Phase 1: [Phase Name]
- [ ] **1.1** [Specific, atomic task]
- [ ] **1.2** [Specific, atomic task]
- [ ] **1.3** [Specific, atomic task]
### Phase 2: [Phase Name]
- [ ] **2.1** [Higher-level task the implementer will break down]
- [ ] **2.2** [Higher-level task]If the spec replaces an old code path with a new one, write separate phases:
1. Build the new path (waves 1 to N)
2. Stop importing the old path (one wave; old code stays on disk, unused)
3. Verify (typecheck, tests, smoke) (one wave; rollback is one revert)
4. Delete the old path (final cleanup wave)Do not schedule deletion before verification passes. greenfield-clean-breaks owns the full Build, Prove, Remove rationale.
List scenarios that might break assumptions or need special handling.
## Edge Cases
### [Scenario Name]
1. [Initial condition]
2. [What happens]
3. [Expected outcome or "See Open Questions"]
### [Scenario Name]
1. [Initial condition]
2. [What happens]
3. [Expected outcome]This section signals "you decide this" to the implementer. Include your recommendation but don't close the question.
## Open Questions
1. **[Question about an unresolved design decision]**
- Options: (a) [Option A], (b) [Option B], (c) [Option C]
- **Recommendation**: [Your suggestion and why, but explicitly leave it open]
2. **[Another unresolved question]**
- [Context about why this is uncertain]
- **Recommendation**: [Suggestion or "Defer until X"]Most specs do not need this section; a spec is an execution spine, not a file whitelist. When adjacent work matters, write only what the implementer needs: deferred items (not required now, allowed later) and opportunistic ones (acceptable to fix if discovered and grounded).
## Adjacent Work
- [Decision or feature]: [Why it is not required now, and what would bring it back.]
- [Issue or cleanup]: [Why it is not required, but may be fixed if discovered and grounded.]Use this section only for Class 3 keeps. Each entry must name the constraint and a revisit trigger.
## Decisions Log
- Keep `[name]`: [constraint or trade-off].
Revisit when: [specific signal that would make the keep decision worth re-opening].How do we know this is done? Checkboxes for verification.
## Success Criteria
- [ ] [Specific, verifiable outcome]
- [ ] [Specific, verifiable outcome]
- [ ] [Tests pass / build succeeds / docs updated]Files that will be touched or consulted.
## References
- `[path/to/file.ts]` - [Why this file is relevant]
- `[path/to/pattern.ts]` - [Pattern to follow or reference]If your spec is too prescriptive, the agent will blindly follow it. If it's too vague, the agent will flounder. The sweet spot is: enough context to start, enough openness to own the implementation.
cb12bcc
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.