Write Your First Spec
Build a real spec for a Todo API. By the end you will have a file that any SpecForge-compatible engine can parse, validate, and execute.
The smallest valid spec
A SpecForge file is JSON, YAML, or TOML. Here is the absolute minimum — a project with one specification, one epic, and one ticket:
{
"specforge": "1.0",
"project": {
"name": "todo-api",
"description": "A simple Todo REST API"
},
"specifications": [
{
"id": "spec-todo-api",
"title": "Todo API",
"status": "draft",
"epics": [
{
"id": "epic-core",
"title": "Core CRUD",
"tickets": [
{
"id": "ticket-create-todo",
"title": "POST /todos",
"status": "open",
"priority": "high",
"estimate": "S"
}
]
}
]
}
]
}Save this as todo-api.sf.json. That's a valid SpecForge spec. Everything else builds on this skeleton.
Adding a Specification with Patterns
Patterns capture architectural decisions that apply across tickets. They prevent style drift by giving agents a single source of truth for conventions.
{
"id": "spec-todo-api",
"title": "Todo API",
"status": "draft",
"patterns": [
{
"id": "pattern-rest-conventions",
"title": "REST Conventions",
"description": "All endpoints follow JSON:API style responses.",
"rules": [
"Use plural nouns for resource URLs",
"Return 201 for successful POST, 200 for GET/PUT, 204 for DELETE",
"Wrap response data in a { \"data\": ... } envelope"
]
},
{
"id": "pattern-error-format",
"title": "Error Format",
"description": "Consistent error shape across all endpoints.",
"rules": [
"Return { \"error\": { \"code\": string, \"message\": string } }",
"Use HTTP status codes correctly — 400 for validation, 404 for missing, 500 for server errors"
]
}
]
}Blueprints
Blueprints are design artifacts — diagrams, schemas, wireframes — referenced by tickets. They keep design decisions discoverable instead of buried in chat logs or document folders.
"blueprints": [
{
"id": "blueprint-db-schema",
"title": "Database Schema",
"type": "schema",
"content": "CREATE TABLE todos (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n title TEXT NOT NULL,\n completed BOOLEAN DEFAULT false,\n created_at TIMESTAMPTZ DEFAULT now()\n);",
"format": "sql"
},
{
"id": "blueprint-api-contract",
"title": "API Contract",
"type": "api",
"content": "openapi: 3.0.0\npaths:\n /todos:\n get:\n summary: List all todos",
"format": "yaml"
}
]Tickets — The atomic unit of agent work
A ticket is the smallest piece of work in a SpecForge spec. It has a status, a priority, an estimate, and optionally references patterns and blueprints so the implementing agent knows what to follow and what to build against.
{
"id": "ticket-create-todo",
"title": "POST /todos",
"description": "Create a new todo item. Validate title is non-empty. Return 201 with the created resource.",
"status": "open",
"priority": "high",
"estimate": "S",
"patternRefs": ["pattern-rest-conventions", "pattern-error-format"],
"blueprintRefs": ["blueprint-db-schema", "blueprint-api-contract"],
"acceptanceCriteria": [
"POST /todos with { \"title\": \"Buy milk\" } returns 201",
"Response body matches { \"data\": { \"id\": string, \"title\": string, \"completed\": false } }",
"POST /todos with empty title returns 400 with error envelope"
]
}Dependencies — The execution graph
Tickets can declare dependencies on other tickets. SpecForge supports two dependency types:
requires— this ticket needs the other to be complete before it can start.blocks— this ticket prevents the other from starting until it is complete.
{
"id": "ticket-list-todos",
"title": "GET /todos",
"status": "open",
"priority": "high",
"estimate": "S",
"dependencies": [
{
"ticketId": "ticket-create-todo",
"type": "requires"
}
]
},
{
"id": "ticket-delete-todo",
"title": "DELETE /todos/:id",
"status": "open",
"priority": "medium",
"estimate": "S",
"dependencies": [
{
"ticketId": "ticket-create-todo",
"type": "requires"
}
]
}How an engine sees this spec
The dependency graph for our Todo API looks like this. An engine walks the graph to determine what can be worked on next.
ticket-create-todo has no dependencies, so it is actionable immediately. The other two require it, so they wait.