SpecForge
FORMAT
v1.0
Getting Started/Write Your First Spec

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"
      ]
    }
  ]
}
Why this matters: Without shared patterns, each agent (or developer) invents their own conventions. The spec becomes inconsistent over time. See: Style Drift →

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"
  }
]
Why this matters: Design artifacts that live outside the spec get lost or go stale. Blueprints keep them version-controlled and linked to the tickets that implement them. See: Orphan Design Artifacts →

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"
  ]
}
Why this matters: Agents hallucinate requirements when the spec is vague. Acceptance criteria give them a concrete checklist to verify against. See: Hallucinated Requirements →

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"
    }
  ]
}
Why this matters: Without explicit dependency data, an engine might start a ticket whose prerequisites are not done yet. The validator catches circular dependencies and dangling references at lint time. See: Work That Shouldn't Exist Yet →

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
ticket-list-todos
ticket-delete-todo

ticket-create-todo has no dependencies, so it is actionable immediately. The other two require it, so they wait.