> ## Documentation Index
> Fetch the complete documentation index at: https://docs.oktolabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Specs

> Reference for the 24 MCP tools that build the spec contract — lifecycle, acceptance criteria, business rules, test scenarios, decisions, API contracts, and the task-traceability links that bind cards back to spec content.

# Specs

The spec is the implementation contract — the artifact that answers "what must be true when this ships?" with enough precision that an agent can implement it without guessing. Pulse exposes **24 of its 216 MCP tools** for spec authoring:

* **Lifecycle (8)** — create, get, get-context, list, update, move, delete, link-card.
* **Test scenarios (2)** — add and list.
* **Business rules (4)** — add, update, remove, list.
* **Decisions (5)** — add, update, remove, link-to-task, migrate.
* **API contracts (4)** — add, update, remove, list.
* **Traceability links (4)** — `link_task_to_scenario`, `link_task_to_rule`, `link_task_to_contract`, `link_task_to_tr`.
* **Spec history (1)** — paginated edit log.

This page covers spec authoring. The two gates that govern spec progression — **spec evaluation** (qualitative score, `validated → in_progress`) and **spec validation** (threshold checks, `approved → validated`) — live on the [Validation gates](/reference/mcp/validation) page. Q\&A on specs lives on [Comments & questions](/reference/mcp/comments-questions); spec knowledge entries live on [Knowledge & attachments](/reference/mcp/knowledge-attachments).

Source-of-truth citations: `okto-pulse-feature-inventory.md:333–352` (spec lifecycle + traceability), `:411–428` (business rules, decisions, API contracts), `:967–981` (status enums), `:982–1021` (governance gates).

***

## Why specs are the richest artifact in the pipeline

The spec is where most of the Knowledge Graph value gets created. Every `add_decision` call with rationale and alternatives becomes a queryable `Decision` node future agents can retrieve via `kg_find_similar_decisions` before making the same call again. Every `add_business_rule` becomes a `Constraint` node tied back to the spec; every `add_test_scenario` becomes a `TestScenario` node linked to its acceptance criterion.

A strong spec includes, at minimum:

* Title + description
* Functional requirements
* Technical requirements
* **Acceptance criteria**, each linked to at least one test scenario
* **Business rules**, each linkable to a card that implements it
* **Test scenarios** with structured evidence on close
* **Decisions** with rationale and alternatives
* **API contracts** (when the spec exposes an endpoint)
* Non-goals (explicit scope exclusion)

The list above maps directly to the tools in this domain.

***

## Status flow and gates

```text theme={null}
draft ──► review ──► approved ──► validated ──► in_progress ──► done
```

| Transition                | MCP tool               | Gate                                                                                                                                                           |
| ------------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `draft → review`          | `okto_pulse_move_spec` | content present (FR, TR, ACs)                                                                                                                                  |
| `review → approved`       | `okto_pulse_move_spec` | manual approval (board permission)                                                                                                                             |
| `approved → validated`    | `okto_pulse_move_spec` | `submit_spec_validation` recorded — see [Validation gates](/reference/mcp/validation)                                                                          |
| `validated → in_progress` | `okto_pulse_move_spec` | `submit_spec_evaluation` score ≥ threshold (default 80) — see [Validation gates](/reference/mcp/validation)                                                    |
| `in_progress → done`      | `okto_pulse_move_spec` | 4 coverage checks + 2 structural checks (ACs covered by scenarios; FR→BR linked; BR→Card linked; AC→Scenario linked; decision coverage; no open non-bug tasks) |

<Note>
  Once a spec is in `validated`, its content is **locked**. Edits require an explicit `validated_to_draft` move, which requires the dedicated permission flag and a justification. This is what makes a spec a contract rather than a wiki page.
</Note>

***

## Tool index

<CardGroup cols={2}>
  <Card title="Lifecycle (8)" icon="file-text">
    `create_spec`, `get_spec`, `get_spec_context`, `list_specs`, `update_spec`, `move_spec`, `delete_spec`, `link_card_to_spec`.
  </Card>

  <Card title="Test scenarios (2)" icon="flask">
    `add_test_scenario`, `list_test_scenarios`. Status updates and the test-theater gate live on [Validation gates](/reference/mcp/validation).
  </Card>

  <Card title="Business rules (4)" icon="shield-check">
    `add`, `update`, `remove`, `list_business_rules`.
  </Card>

  <Card title="Decisions (5)" icon="brain">
    `add`, `update`, `remove`, `link_task_to_decision`, `migrate_spec_decisions` (legacy → structured Decision model).
  </Card>

  <Card title="API contracts (4)" icon="plug">
    `add`, `update`, `remove`, `list_api_contracts`.
  </Card>

  <Card title="Traceability links (4)" icon="link">
    `link_task_to_scenario`, `link_task_to_rule`, `link_task_to_contract`, `link_task_to_tr` — bind cards back to spec content for the `→ done` coverage gate.
  </Card>
</CardGroup>

***

## Lifecycle

### `okto_pulse_create_spec`

Source: `server.py:5462`. Create a spec with title, acceptance criteria, FR, TR, BRs, and initial test scenarios. Most agents skip this in favour of `derive_spec_from_ideation` / `derive_spec_from_refinement` (see [Ideation & refinement](/reference/mcp/ideation-refinement)) so the spec inherits upstream context.

### `okto_pulse_get_spec`

Source: `server.py:5564`. Get a spec with all linked artifacts.

### `okto_pulse_get_spec_context`

Source: `server.py:5624`. **Use this — not `get_spec` — before authoring or advancing a spec.** The bundle includes the spec plus its sprint, cards, KG decisions, business rules, and API contracts in one call.

### `okto_pulse_list_specs`

Source: `server.py:5791`. List specs on a board, filtered by status or parent ideation.

### `okto_pulse_update_spec`

Source: `server.py:5850`. Update content fields (acceptance criteria, FR, TR, BRs, scenarios, contracts). Calling this on a `validated` spec returns `CONFLICT` — unlock first via `move_spec` to `validated_to_draft`.

<CodeGroup>
  ```text Input theme={null}
  spec_id: "spc_b921..."
  title:   "Per-user rate limiting on /search"
  description: "Burst 20 req/s, sustained 5 req/s. 429 + Retry-After when exceeded."
  acceptance_criteria: [
    "AC1: 429 returned within 5ms when over the burst limit.",
    "AC2: Retry-After header reflects current bucket reset time.",
    "AC3: Limits scoped per authenticated user_id; anonymous traffic uses source IP."
  ]
  non_goals: [
    "Global per-route rate limiting (out of scope; covered by gateway)."
  ]
  ```

  ```json Output theme={null}
  {
    "id": "spc_b921...",
    "status": "draft",
    "title": "Per-user rate limiting on /search",
    "description": "Burst 20 req/s, sustained 5 req/s. ...",
    "acceptance_criteria": ["AC1: ...", "AC2: ...", "AC3: ..."],
    "non_goals": ["Global per-route rate limiting (...)."],
    "updated_at": "2026-05-07T15:14:00Z"
  }
  ```
</CodeGroup>

### `okto_pulse_move_spec`

Source: `server.py:5946`. Advance status. Each of the four forward gates is server-enforced; backward transitions (`approved_to_draft`, `validated_to_draft`) require dedicated permission flags.

### `okto_pulse_delete_spec`

Source: `server.py:10021`. Permanently delete a spec. Linked cards and their conclusions remain — the foreign key is set to `NULL`, not cascaded. Prefer `archive_tree` for soft-delete with restore.

### `okto_pulse_link_card_to_spec`

Source: `server.py:10052`. Attach an existing card to a spec. Useful when a sprint is created on one spec and a card from another spec needs to roll up into the same delivery.

***

## Test scenarios (authoring)

Test scenarios are the bridge between **acceptance criteria** (what the agent claims) and **evidence** (what an implementer produces at close). The status update tool — and its anti-theater gate — lives on the [Validation gates](/reference/mcp/validation) page.

### `okto_pulse_add_test_scenario`

Source: `server.py:6010`. Add a test scenario with required acceptance-criterion linkage. A spec without 100 % AC → scenario coverage cannot pass the `→ done` gate.

<CodeGroup>
  ```text Input theme={null}
  spec_id: "spc_b921..."
  title:   "Burst limit returns 429 within 5ms"
  description: "Send 25 requests in 1 second; expect 5 success + 20 with status=429 and a Retry-After header."
  acceptance_criteria: ["AC1"]
  preconditions: "Authenticated user; Redis available."
  expected_outcome: "Latency-to-429 ≤ 5ms p95."
  ```

  ```json Output theme={null}
  {
    "id": "tsc_8a1c...",
    "spec_id": "spc_b921...",
    "title": "Burst limit returns 429 within 5ms",
    "status": "pending",
    "acceptance_criteria": ["AC1"],
    "evidence": null,
    "added_at": "2026-05-07T15:18:42Z"
  }
  ```
</CodeGroup>

### `okto_pulse_list_test_scenarios`

Source: `server.py:6105`. List test scenarios on a spec with their evidence and status.

***

## Business rules

Business rules capture invariants the implementation must honour. Each rule should be linkable to one or more cards via `link_task_to_rule` — that's what makes the FR → BR → Card linkage gate satisfiable.

| Tool                              | Line             | Description                                          |
| --------------------------------- | ---------------- | ---------------------------------------------------- |
| `okto_pulse_add_business_rule`    | `server.py:8260` | Add a rule with description, rationale, and category |
| `okto_pulse_update_business_rule` | `server.py:8347` | Update an existing rule                              |
| `okto_pulse_remove_business_rule` | `server.py:8442` | Remove a rule from a spec                            |
| `okto_pulse_list_business_rules`  | `server.py:8953` | List all rules on a spec                             |

<CodeGroup>
  ```text add_business_rule — Input theme={null}
  spec_id:     "spc_b921..."
  description: "On Redis outage, allow all requests (fail open) — never block on infra failure."
  rationale:   "Search outage is more visible than rate-limit bypass; pager noise > minor abuse window."
  category:    "reliability"
  ```

  ```json add_business_rule — Output theme={null}
  {
    "id": "br_3da7...",
    "spec_id": "spc_b921...",
    "description": "On Redis outage, allow all requests (fail open) — never block on infra failure.",
    "rationale": "Search outage is more visible than rate-limit bypass; ...",
    "category": "reliability",
    "added_at": "2026-05-07T15:22:11Z"
  }
  ```
</CodeGroup>

***

## Decisions

A `Decision` is a formalized design choice with rationale, alternatives considered, and consequences. Decisions are first-class KG nodes — past decisions are retrievable via `kg_find_similar_decisions` before an agent makes a similar call.

| Tool                                | Line             | Description                                                               |
| ----------------------------------- | ---------------- | ------------------------------------------------------------------------- |
| `okto_pulse_add_decision`           | `server.py:8523` | Record a decision with rationale + alternatives                           |
| `okto_pulse_update_decision`        | `server.py:8630` | Update a decision                                                         |
| `okto_pulse_remove_decision`        | `server.py:8730` | Remove a decision from a spec                                             |
| `okto_pulse_link_task_to_decision`  | `server.py:8783` | Link a card to a decision it implements                                   |
| `okto_pulse_migrate_spec_decisions` | `server.py:8852` | Migrate inline spec decisions (legacy text) into structured Decision rows |

<CodeGroup>
  ```text add_decision — Input theme={null}
  spec_id:    "spc_b921..."
  title:      "Use Redis token-bucket for rate-limit storage"
  context:    "Need ≤ 1ms p99 enforcement; Redis already in stack; leaky-bucket adds 6ms overhead."
  decision:   "Use Redis token-bucket via INCRBYFLOAT + EXPIRE."
  rationale:  "Existing Redis cluster, sub-millisecond enforcement, atomic increment primitive."
  alternatives: [
    {"name": "In-memory leaky-bucket", "reason_rejected": "Lost on restart; +6ms p99 overhead."},
    {"name": "Sliding window log", "reason_rejected": "O(N) per request; doesn't scale to /search QPS."}
  ]
  consequences: "Hard dependency on Redis 7.x cluster mode; fail-open required (see BR br_3da7)."
  ```

  ```json add_decision — Output theme={null}
  {
    "id": "dec_e7ca...",
    "spec_id": "spc_b921...",
    "title": "Use Redis token-bucket for rate-limit storage",
    "context": "Need ≤ 1ms p99 enforcement; ...",
    "decision": "Use Redis token-bucket via INCRBYFLOAT + EXPIRE.",
    "rationale": "Existing Redis cluster, sub-millisecond enforcement, ...",
    "alternatives": [
      {"name": "In-memory leaky-bucket", "reason_rejected": "Lost on restart; +6ms p99 overhead."},
      {"name": "Sliding window log",     "reason_rejected": "O(N) per request; ..."}
    ],
    "consequences": "Hard dependency on Redis 7.x cluster mode; ...",
    "added_at": "2026-05-07T15:25:33Z"
  }
  ```
</CodeGroup>

<Tip>
  Always populate `alternatives` with at least one rejected option and a real reason. A `Decision` with empty alternatives carries no signal in the KG — future agents see "we made this choice" but not "and why we didn't make another."
</Tip>

***

## API contracts

Use API contracts to encode endpoints the spec exposes (or modifies). The structured shape unlocks future OpenAPI export and is queryable via the KG.

| Tool                             | Line             | Description                                                                      |
| -------------------------------- | ---------------- | -------------------------------------------------------------------------------- |
| `okto_pulse_add_api_contract`    | `server.py:9007` | Add a contract (endpoint, method, request schema, response schema, status codes) |
| `okto_pulse_update_api_contract` | `server.py:9138` | Update a contract                                                                |
| `okto_pulse_remove_api_contract` | `server.py:9276` | Remove a contract                                                                |
| `okto_pulse_list_api_contracts`  | `server.py:9322` | List contracts on a spec                                                         |

<CodeGroup>
  ```text add_api_contract — Input theme={null}
  spec_id: "spc_b921..."
  endpoint: "/api/v1/search"
  method:   "GET"
  request_schema: {"query": "string", "page": "int?"}
  response_schema: {
    "200": {"results": "list[SearchHit]", "next_page": "int?"},
    "429": {"error": "RATE_LIMITED", "retry_after": "int (seconds)"}
  }
  notes: "Adds 429 case to existing endpoint; existing 200 contract unchanged."
  ```

  ```json add_api_contract — Output theme={null}
  {
    "id": "apc_5b18...",
    "spec_id": "spc_b921...",
    "endpoint": "/api/v1/search",
    "method": "GET",
    "request_schema": {"query": "string", "page": "int?"},
    "response_schema": {
      "200": {"results": "list[SearchHit]", "next_page": "int?"},
      "429": {"error": "RATE_LIMITED", "retry_after": "int (seconds)"}
    },
    "notes": "Adds 429 case to existing endpoint; existing 200 contract unchanged.",
    "added_at": "2026-05-07T15:30:18Z"
  }
  ```
</CodeGroup>

***

## Traceability links

The four `link_task_to_*` tools bind cards back to spec content. Each one satisfies a piece of the `→ done` coverage gate.

| Tool                               | Line             | Description                                             |
| ---------------------------------- | ---------------- | ------------------------------------------------------- |
| `okto_pulse_link_task_to_scenario` | `server.py:6461` | Link a card to a test scenario it implements / verifies |
| `okto_pulse_link_task_to_rule`     | `server.py:6544` | Link a card to a business rule it satisfies             |
| `okto_pulse_link_task_to_contract` | `server.py:6606` | Link a card to an API contract it implements            |
| `okto_pulse_link_task_to_tr`       | `server.py:6666` | Link a card to a technical requirement                  |

<Tip>
  Use these aggressively during sprint planning, not at close. The `→ done` gate fails for any FR / BR / contract / scenario without a matching task link — making the linkage retroactive is more painful than recording it as cards are created.
</Tip>

***

## Spec history

### `okto_pulse_get_spec_history`

Source: `server.py:10351`. Paginated per-field edit log for a spec.

***

## Permissions

| Action                                                                | Permission flag                   |
| --------------------------------------------------------------------- | --------------------------------- |
| Create spec                                                           | `Permissions.SPECS_CREATE`        |
| Update content (any of: AC, FR, TR, BR, scenario, contract, decision) | `Permissions.SPECS_UPDATE`        |
| Move forward                                                          | `Permissions.SPECS_MOVE`          |
| Move backward (`validated_to_draft`, `approved_to_draft`)             | `Permissions.SPECS_MOVE_BACKWARD` |
| Delete spec                                                           | `Permissions.SPECS_DELETE`        |
| Add / update / remove decisions                                       | `Permissions.SPECS_UPDATE`        |
| Link cards to spec content                                            | `Permissions.SPECS_LINK_TASK`     |
| Read spec / list / context                                            | `Permissions.BOARD_READ`          |

For the gate-submission tools (`submit_spec_evaluation`, `submit_spec_validation`), see the [Validation gates](/reference/mcp/validation) page.

***

## Errors

```json theme={null}
{
  "error": "CONFLICT",
  "message": "Cannot update spec — status is 'validated'. Move to 'validated_to_draft' first to unlock content edits.",
  "detail": {"current_status": "validated"}
}
```

| Code               | Most common cause                                                                                                                |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `VALIDATION_ERROR` | Acceptance criteria empty when moving to `review`; decision missing `alternatives`; API contract missing `method` or `endpoint`. |
| `NOT_FOUND`        | Spec / scenario / rule / decision / contract ID belongs to a different board.                                                    |
| `FORBIDDEN`        | Agent's preset does not include the required permission flag.                                                                    |
| `CONFLICT`         | Editing a `validated` spec without first moving it back to draft; advancing a spec that has not satisfied its current gate.      |

***

## Next steps

<CardGroup cols={2}>
  <Card title="Validation gates" href="/reference/mcp/validation">
    `submit_spec_evaluation`, `submit_spec_validation`, `submit_task_validation`, and the test-theater anti-pattern enforcement.
  </Card>

  <Card title="Sprints & cards" href="/reference/mcp/sprints-cards">
    Slice the spec into reviewable deliveries; create and move cards through implementation.
  </Card>

  <Card title="Knowledge & attachments" href="/reference/mcp/knowledge-attachments">
    Attach reference docs and binaries to a spec.
  </Card>

  <Card title="ADLC pipeline" href="/concepts/sdlc-flow">
    Where the spec sits in the end-to-end flow.
  </Card>
</CardGroup>
