---
title: Capability Requests
---
```js
import {API, apiFetch, pollDelay, waitForVisible} from "./components/config.js";
const POLL = 30_000;
```
```js
// Live poll for capability requests
const reqState = (async function*() {
let failures = 0;
while (true) {
let data = [], ok = false;
try {
const r = await apiFetch("/capability-requests/");
ok = r.ok;
data = ok ? await r.json() : [];
} catch {}
failures = ok ? 0 : failures + 1;
yield {data, ok, ts: new Date()};
await waitForVisible(pollDelay({ok, base: POLL, failures}));
}
})();
```
```js
const requests = reqState.data ?? [];
const _ok = reqState.ok ?? false;
const _ts = reqState.ts;
```
# Capability Requests
```js
import {injectTocTop} from "./components/toc-sidebar.js";
import {withDocHelp} from "./components/doc-overlay.js";
const _liveEl = html`
●
${_ok ? `Live · ${_ts?.toLocaleTimeString()}` : html`API offline`}
`;
withDocHelp(_liveEl, "/docs/live-data");
injectTocTop("live-indicator", _liveEl);
const _h1 = document.querySelector("#observablehq-main h1");
if (_h1) { _h1.style.position = "relative"; withDocHelp(_h1, "/docs/capabilities"); }
```
```js
// KPI sidebar
const open = requests.filter(r => ["requested","routing_disputed","accepted","in_progress","ready_for_review"].includes(r.status));
const completed = requests.filter(r => r.status === "completed");
const avgFulfill = completed.length > 0
? (completed.reduce((s, r) => s + (new Date(r.completed_at) - new Date(r.created_at)), 0) / completed.length / 86400000).toFixed(1)
: "—";
const critical = open.filter(r => r.priority === "critical" || r.priority === "high").length;
const kpiEl = html`
Capability Requests
Open${open.length}
Avg fulfill${avgFulfill}d
High/Critical${critical}
Total${requests.length}
`;
injectTocTop("cap-req-kpi", kpiEl);
```
```js
// Filters
const typeFilter = Inputs.select(
["all", ...new Set(requests.map(r => r.capability_type))],
{label: "Type", value: "all"}
);
const statFilter = Inputs.select(
["all", "routing_disputed", "requested", "accepted", "in_progress", "ready_for_review", "completed", "rejected", "withdrawn"],
{label: "Status", value: "all"}
);
const domFilter = Inputs.select(
["all", ...new Set([...requests.map(r => r.requesting_domain_slug), ...requests.map(r => r.fulfilling_domain_slug).filter(Boolean)])],
{label: "Domain", value: "all"}
);
display(html`
${typeFilter}${statFilter}${domFilter}
`);
```
```js
const tf = typeFilter.value;
const sf = statFilter.value;
const df = domFilter.value;
const filtered = requests.filter(r =>
(tf === "all" || r.capability_type === tf) &&
(sf === "all" || r.status === sf) &&
(df === "all" || r.requesting_domain_slug === df || r.fulfilling_domain_slug === df)
);
```
## Summary
```js
const priorityColors = {critical: "#e53935", high: "orange", medium: "steelblue", low: "#aaa"};
const disputed = requests.filter(r => r.status === "routing_disputed");
// Disputed banner — shown at top when any exist
if (disputed.length > 0) {
display(html`
⚠ Routing Disputed (${disputed.length})
${disputed.map(r => html`
Dispute: ${r.dispute_reason ?? "(no reason given)"}
${r.dispute_suggested_domain ? html`
Suggested domain: ${r.dispute_suggested_domain}
` : ""}
${r.disputed_by ? html`
Raised by ${r.disputed_by} · ${new Date(r.disputed_at).toLocaleString()}
` : ""}
`)}
`);
}
display(html`
Requested
${requests.filter(r => r.status === "requested").length}
In Progress
${requests.filter(r => ["accepted","in_progress"].includes(r.status)).length}
Ready for Review
${requests.filter(r => r.status === "ready_for_review").length}
Completed
${completed.length}
`);
```
## Status Kanban
```js
const statusCols = [
{key: "routing_disputed", label: "⚠ Routing Disputed", color: "#f59e0b"},
{key: "requested", label: "Requested", color: "steelblue"},
{key: "accepted", label: "Accepted", color: "#f0a500"},
{key: "in_progress", label: "In Progress", color: "#2196f3"},
{key: "ready_for_review", label: "Ready for Review", color: "#4caf50"},
{key: "completed", label: "Completed", color: "#2e7d32"},
{key: "rejected", label: "Rejected", color: "#e53935"},
{key: "withdrawn", label: "Withdrawn", color: "#bbb"},
];
const colMap = {};
for (const r of filtered) {
(colMap[r.status] = colMap[r.status] ?? []).push(r);
}
const activeCols = statusCols.filter(s => colMap[s.key]?.length);
if (activeCols.length === 0) {
display(html`No capability requests match the current filters.
`);
} else {
const ageDays = (r) => ((Date.now() - new Date(r.created_at)) / 86400000).toFixed(0);
display(html`
${activeCols.map(s => html`
${colMap[s.key].map(r => html`
${r.capability_type}
${r.priority}
${r.title}
${r.requesting_domain_slug}
${r.fulfilling_domain_slug ? html` → ${r.fulfilling_domain_slug}` : html` → unassigned`}
${ageDays(r)}d old
`)}
`)}
`);
}
```
## All Requests
```js
display(Inputs.table(filtered.map(r => ({
Type: r.capability_type,
Title: r.title,
Priority: r.priority,
Status: r.status,
Requester: r.requesting_domain_slug,
Provider: r.fulfilling_domain_slug ?? "—",
Agent: r.requesting_agent,
Created: new Date(r.created_at).toLocaleDateString(),
})), {maxWidth: 1000}));
```
---
## Capability Catalog
```js
// Live poll for catalog entries
const catalogState = (async function*() {
let failures = 0;
while (true) {
let data = [], ok = false;
try {
const r = await apiFetch("/capability-catalog/?status=all");
ok = r.ok;
if (r.ok) data = await r.json();
} catch {}
failures = ok ? 0 : failures + 1;
yield data;
await waitForVisible(pollDelay({ok, base: POLL, failures}));
}
})();
```
```js
const catalog = catalogState ?? [];
```
```js
if (catalog.length === 0) {
display(html`No capabilities registered yet. Add ```capability blocks to SCOPE.md files and run make ingest-capabilities-all.
`);
} else {
// Group by domain
const byDomain = {};
for (const c of catalog) {
(byDomain[c.domain_slug] = byDomain[c.domain_slug] ?? []).push(c);
}
const typeColors = {
infrastructure: "#e65100", api: "#1565c0", data: "#2e7d32",
security: "#c62828", documentation: "#6a1b9a", other: "#888"
};
display(html`
${Object.entries(byDomain).sort((a, b) => a[0].localeCompare(b[0])).map(([domain, caps]) => html`
${domain} ${caps.length}
${caps.map(c => html`
${c.capability_type}
${c.title}
${c.description ? html`
${c.description}
` : ""}
${c.keywords?.length ? html`
${c.keywords.map(k => html`${k}`)}
` : ""}
`)}
`)}
`);
}
```