feat(capability-requests): add cross-domain capability catalog and request routing

Introduces a capability catalog (CUST-WP-0022) so domains can advertise what
they provide and agents can request capabilities from other domains with
auto-routing, lifecycle tracking, and task-unblocking on completion.

- New models: CapabilityCatalog, CapabilityRequest with full lifecycle
  (requested → accepted → in_progress → ready_for_review → completed/rejected/withdrawn)
- Migration i6d7e8f9a0b1: capability_catalog + capability_requests tables
- Router /capability-catalog and /capability-requests with accept/status endpoints
- 7 new MCP tools: register_capability, list_capabilities, request_capability,
  accept_capability_request, update_capability_request_status,
  list_capability_requests, get_capability_request
- StateSummary gains open_capability_requests count
- Dashboard: capability-requests.md page + docs/capabilities.md + docs/scope.md
- SCOPE.md: three seed capabilities documented (MCP registration, state tracking, SBOM)
- scope.template: Provided Capabilities section with example block
- scripts/ingest_capabilities.py + make ingest-capabilities[/-all] targets

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 21:07:50 +01:00
parent 7bf3cf583a
commit d45234531b
18 changed files with 2105 additions and 1 deletions

View File

@@ -0,0 +1,228 @@
---
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 lifecycle
```
requested → accepted → in_progress → ready_for_review → completed
↓ ↓ ↓ ↓
withdrawn rejected withdrawn withdrawn
withdrawn
```
| Status | 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 |
Transitions are enforced by the API — you cannot skip stages. Terminal states
(`completed`, `rejected`, `withdrawn`) allow no further transitions.
---
## Auto-notifications
Every lifecycle transition 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 `blocked` → `todo` and clears its `blocking_reason`. This means
blocked 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 lifecycle:
```
update_capability_request_status(request_id, "in_progress")
update_capability_request_status(request_id, "ready_for_review", note="Instance up at 10.0.1.42")
```
The requester reviews and completes:
```
update_capability_request_status(request_id, "completed", note="Verified, looks good")
```
---
## 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 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** | Workstream-to-workstream 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` and `get_next_steps`. They do not originate in workplan files —
they are operational coordination.*

177
dashboard/src/docs/scope.md Normal file
View File

@@ -0,0 +1,177 @@
---
title: SCOPE.md — Reference
---
# SCOPE.md — Reference
SCOPE.md is a lightweight, strategic orientation artifact placed at the root of
every registered repository. It helps humans and agents quickly understand what
a repo is about, when it is relevant, and where it fits in the ecosystem.
---
## What SCOPE.md is
SCOPE.md answers these questions **in under 60 seconds**:
- What is this repository for?
- Should I care about it right now?
- When is it relevant to my work?
- Where does it fit in the ecosystem?
- Is it mature enough to trust or reuse?
- Does it overlap with something else?
It is **not** a README, not architecture documentation, and not marketing text.
It is a pragmatic, scannable boundary definition.
---
## Template structure
Every SCOPE.md follows an 11-section template:
| Section | Purpose |
|---------|---------|
| **One-liner** | One precise sentence describing the repo's purpose |
| **Core Idea** | Main capability and what problem it solves |
| **In Scope** | What the repo is explicitly responsible for — concrete, not vague |
| **Out of Scope** | What it deliberately does NOT do (often more important) |
| **Relevant When** | Real usage scenarios when someone should consider this repo |
| **Not Relevant When** | When someone should look elsewhere |
| **Current State** | Maturity indicators: status, implementation, stability, usage |
| **How It Fits** | Upstream dependencies, downstream consumers, often-used-with |
| **Terminology** | Domain terms, potential confusions with similar concepts |
| **Related / Overlapping** | Repos with similar or adjacent responsibilities |
| **Provided Capabilities** | What this repo's domain can provide to others on request |
| **Getting Oriented** | Entry points, key files, where to start |
The template is at `state-hub/scripts/project_rules/scope.template`.
---
## Current State indicators
The Current State section uses four axes:
| Axis | Values |
|------|--------|
| **Status** | concept / experimental / active / stable / deprecated |
| **Implementation** | idea / partial / substantial / complete |
| **Stability** | unstable / evolving / stable |
| **Usage** | none / personal / internal / production |
These help an agent decide whether to depend on, extend, or avoid a repo
without needing to read its full codebase.
---
## Design principles
- **Intentionally short and scannable** — not comprehensive documentation
- **Pragmatic** — real usage scenarios, not ideals
- **Easy to maintain** — update when scope changes, not on every commit
- **Direct language** — no filler, no marketing, no invented features
- **Honest about gaps** — if something is incomplete or unstable, say so
**Anti-goals:**
- No long prose or verbose explanations
- No repetition of README content
- No hiding ambiguity behind vague language
- No assumption of production readiness
---
## How SCOPE.md is created
### New repositories
When a repo is registered via `make register-project`, the scaffold copies
`scope.template``SCOPE.md` at the repo root. The human or an agent then
populates the sections from the repo's actual state.
### Existing repositories
The **scope-analyst** kaizen agent persona can be loaded to generate or refine
a SCOPE.md:
```
get_kaizen_agent("scope-analyst")
```
This agent reads the repo's codebase, existing documentation, and CLAUDE.md
to produce a SCOPE.md that accurately reflects the current state.
---
## Ecosystem coverage
SCOPE.md files exist across all custodian domains:
| Domain | Repos with SCOPE.md |
|--------|-------------------|
| **custodian** | the-custodian, kaizen-agentic, ops-bridge, activity-core |
| **custodian** (netkingdom) | net-kingdom, key-cape |
| **railiance** | railiance-apps, railiance-cluster, railiance-enablement, railiance-infra, railiance-platform |
| **markitect** | markitect_project |
---
## Provided Capabilities section
The `## Provided Capabilities` section uses fenced `capability` blocks that
are machine-readable and ingested into the state-hub capability catalog:
````markdown
```capability
type: infrastructure
title: Cluster provisioning
description: Provision k8s clusters and managed instances for any domain.
keywords: [cluster, k8s, privacy, instance]
```
````
| Field | Required | Purpose |
|-------|----------|---------|
| **type** | yes | Category: `infrastructure`, `api`, `data`, `security`, `documentation`, `other` |
| **title** | yes | Short name (unique within domain + type) |
| **description** | no | What this capability provides |
| **keywords** | no | Routing hints for auto-matching capability requests |
The ingest script (`make ingest-capabilities-all`) parses these blocks from all
registered repos and populates the state-hub catalog table. This follows
ADR-001: **files are the origin of truth, DB is cache/index**.
---
## Relation to capabilities
SCOPE.md is both the **human-readable boundary definition** and the **origin of
truth for the capability catalog**:
| | SCOPE.md | Capability Catalog (DB) |
|-|----------|------------------------|
| **Role** | Origin of truth | Derived index |
| **Granularity** | Per-repository | Per-domain (aggregated from repo files) |
| **Purpose** | "What is this repo?" + "What can it provide?" | Routing engine for capability requests |
| **Updates** | Edit the file, re-ingest | Auto-populated from SCOPE.md |
| **Readable without hub** | Yes — just open the file | No — requires API |
This means a repo is fully self-describing: you can understand what it provides
by reading SCOPE.md alone, without any centralized infrastructure.
---
## Relation to other concepts
| Concept | Relationship |
|---------|-------------|
| **CLAUDE.md** | Build/test/lint instructions — *how* to work with the repo |
| **SCOPE.md** | Boundary definition — *what* the repo is and isn't |
| **Capability Catalog** | Operational routing — *what the domain can provide* on request |
| **Domain Goals** | Strategic direction — *where the domain is heading* |
| **Project Charters** | Founding intent — *why the domain exists* (in `canon/projects/`) |
---
*SCOPE.md is the boundary definition layer. It tells you whether you are in the
right place before you start reading code.*