---
title: Capabilities — Reference
---

# Capabilities — Reference

The Capability Requests page shows cross-domain provisioning requests — a
decoupled mechanism for one domain to request something that another domain is
responsible for, without needing to know *who* is responsible.

---

## What is a capability?

A **capability** is something a domain can provide to the broader ecosystem —
infrastructure provisioning, API endpoints, security tooling, documentation,
data pipelines, etc. Capabilities are registered in the **capability catalog**
so the system knows which domain provides what.

A **capability request** is a structured declaration from a requester
("I need X") that the system routes to the right provider automatically.

---

## Capability catalog

The catalog is the routing backbone. Each entry registers one thing a domain
can provide.

**Origin of truth: SCOPE.md** — following ADR-001, capability declarations
live in each repo's `SCOPE.md` file under the `## Provided Capabilities`
section. The state-hub catalog table is a derived index, reconstructable from
repo files via `make ingest-capabilities-all`.

### SCOPE.md capability blocks

Add fenced `capability` blocks to your repo's SCOPE.md:

````markdown
## Provided Capabilities

```capability
type: infrastructure
title: Cluster provisioning
description: Provision k8s clusters and managed instances for any domain.
keywords: [cluster, k8s, privacy, instance]
```
````

| Field | Purpose |
|-------|---------|
| **type** | Category — `infrastructure`, `api`, `data`, `security`, `documentation`, `other` |
| **title** | Short name (unique within domain + type) |
| **description** | What this capability provides, in one or two sentences |
| **keywords** | Routing hints matched against request descriptions |

### Ingesting into the catalog

```bash
make ingest-capabilities REPO=the-custodian       # single repo
make ingest-capabilities-all                       # all registered repos
make ingest-capabilities REPO=railiance-infra DRY_RUN=1  # preview
```

The ingest script reads `SCOPE.md` → parses `capability` blocks → upserts into
the `capability_catalog` table via the API. Existing entries (same domain + type
+ title) are skipped.

### Browsing the catalog

Via MCP:
```
list_capabilities(domain="railiance")
```

Via API:
```
GET /capability-catalog/?domain=railiance
```

The catalog is also shown at the bottom of the Capabilities dashboard page,
grouped by domain with type badges and keyword tags.

---

## Routing algorithm

When a request is created, the system auto-routes it:

1. **Exact type match** — find catalog entries where `capability_type` matches
2. **Single match** — auto-assign the providing domain
3. **Multiple matches** — keyword-score the request description against each entry's keywords; pick the winner if unambiguous
4. **No match or tie** — leave the provider unassigned and **broadcast** a notification to all domains so one can claim it

This means the requester never needs to know which domain owns a capability.

---

## Request flow

```
requested → accepted → in_progress → ready_for_review → completed
    ↓           ↓           ↓               ↓
 withdrawn   rejected   withdrawn       withdrawn
              withdrawn
```

| Workstation | Meaning |
|--------|---------|
| **requested** | Need declared; routed (or broadcast) to provider |
| **accepted** | Provider acknowledged and claimed the request |
| **in_progress** | Provider is actively working on it |
| **ready_for_review** | Provider finished; requester should review and optimise |
| **completed** | Requester confirmed; capability is live |
| **rejected** | Provider cannot or will not fulfil the request |
| **withdrawn** | Requester cancelled the request |

Request movement is flow-aware. The API evaluates the target workstation's
entry assertions and rejects unreachable movement with machine-readable
blocking assertions. Terminal workstations (`completed`, `rejected`,
`withdrawn`) have no reachable outgoing path in the current flow definition.

---

## Auto-notifications

Every request movement handled by the capability request API creates an
**AgentMessage** atomically:

| Transition | Notification to |
|------------|----------------|
| **requested** | Provider domain agent (or `broadcast` if unrouted) |
| **accepted** | Requesting agent |
| **in_progress** | Requesting agent |
| **ready_for_review** | Requesting agent |
| **completed** | Requesting agent |
| **rejected** | Requesting agent (with reason) |

Notifications appear in the [Inbox](/inbox) page and are queryable via
`get_messages(to_agent="<your-agent>")`.

---

## Auto-unblock

A request can optionally link to a **blocking task** via `blocking_task_id`.
When the request reaches `completed`, the system automatically patches that
task from `wait` → `todo` and clears its `blocking_reason`. This means
waiting work resumes without manual intervention.

---

## Creating a request

Via MCP:

```
request_capability(
  title                    = "Privacy idea instance on cluster",
  description              = "Need a privacy idea instance provisioned on the k8s cluster",
  capability_type          = "infrastructure",
  requesting_agent         = "net-kingdom-worker",
  requesting_domain        = "custodian",
  requesting_workstream_id = "<uuid>",         # optional
  priority                 = "high",           # low | medium | high | critical
  blocking_task_id         = "<task-uuid>"     # optional — auto-unblocked on completion
)
```

The system routes this to `railiance` (if a matching catalog entry exists),
creates an AgentMessage notification, and returns the request with
`fulfilling_domain_slug: "railiance"`.

---

## Accepting and fulfilling

The provider agent checks their inbox, sees the request, and accepts:

```
accept_capability_request(
  request_id               = "<uuid>",
  fulfilling_agent         = "railiance-worker",
  fulfilling_workstream_id = "<uuid>"          # optional
)
```

Then advances through the flow:

```
advance_workstation(entity_type="capability_request", entity_id=request_id, target_workstation="in_progress")
advance_workstation(entity_type="capability_request", entity_id=request_id, target_workstation="ready_for_review")
```

The requester reviews and completes:

```
advance_workstation(entity_type="capability_request", entity_id=request_id, target_workstation="completed")
```

---

## Dashboard

The Capabilities page shows:

- **KPI sidebar** — open count, average fulfillment time, high/critical count
- **Summary cards** — requested, in progress, ready for review, completed
- **Kanban board** — cards grouped by workstation/status column
- **Table** — all requests with filters by type, status, and domain

Each card shows the capability type, priority, requester → provider domains,
and age in days.

---

## Relation to other concepts

| Concept | Relationship |
|---------|-------------|
| **SCOPE.md** | Defines what a repo *is responsible for* — the catalog registers what it *can provide* |
| **Dependencies** | Workplan-to-workplan edges — capabilities are higher-level, domain-to-domain |
| **Extension Points** | Design forks for *future* enhancement — capabilities are *operational* requests |
| **Contributions** | Outbound upstream work — capabilities are *inbound* requests between internal domains |
| **Human Interventions** | Flagged tasks for Bernd — capabilities are agent-to-agent coordination |

---

*Capability requests are a sanctioned write use case of the State Hub alongside
`resolve_decision`, `get_next_steps`, and intake capture (`create_intake`,
`route_intake`, `close_intake`; promotion via `statehub promote-intake`). The
legacy suggestion mutation tools (`create_suggestion`, `vet_suggestion`, …)
return HTTP 410. Capability requests do not originate in workplan files —
they are operational coordination. See [Work Records](/docs/work-records) and
[Intakes](/docs/intakes).*
