Canvas widgets
The war room's canvas is built by the investigator agent from what it reads. Every card is one of the widget types below, rendered from validated data, and every query-backed card re-reads its source for the window you choose.
Widget types
The same catalog drives the agent's add_widget tool, the bridge's share_with_room widget argument, the hosted connector's describe_widget_types read and the canvas renderer. Adding a type anywhere means adding it here.
Stat tile stat
One current number with its direction and baseline, for a headline metric.
- current error rate
- p99 latency now
- healthy host count
- requests per second
- any single KPI
Can be backed by a signed query that re-reads live. Follows the canvas time window.

Data shape
{ value: number, unit?, decimals?, delta?: number, deltaLabel?, trend?: "up"|"down"|"flat", tone?: "good"|"warning"|"serious"|"critical"|"neutral", baseline?: number, baselineLabel?, spark?: number[] }Example
{
"value": 6.7,
"unit": "%",
"decimals": 1,
"delta": 6.4,
"deltaLabel": "↑ 22×",
"trend": "up",
"tone": "critical",
"baseline": 0.3,
"baselineLabel": "baseline 0.3%",
"spark": [
0.3,
0.3,
0.4,
0.3,
2.1,
5.8,
6.4,
6.7
]
}Time series chart
One or more metrics over time on a shared clock axis, with optional thresholds and deploy markers.
- error rate over the incident
- latency trend
- traffic volume
- one metric split by region, zone or service
- comparing a metric before and after a change
Can be backed by a signed query that re-reads live. Follows the canvas time window. Up to 8 series; the rest fold into one "other".

Data shape
{ series: [{ label, unit?, decimals?, points: [{ t: ISO-8601, v: number }] }] (max 8), yAxis?: { min?, max?, unit?, label? }, thresholds?: [{ value, label?, tone? }], markers?: [{ t: ISO-8601, label, kind? }] }Example
{
"series": [
{
"label": "eu-west-1",
"unit": "%",
"points": [
{
"t": "2026-09-04T14:00:00Z",
"v": 0.3
},
{
"t": "2026-09-04T14:15:00Z",
"v": 0.3
},
{
"t": "2026-09-04T14:30:00Z",
"v": 0.4
},
{
"t": "2026-09-04T14:35:00Z",
"v": 9.8
},
{
"t": "2026-09-04T14:45:00Z",
"v": 12.4
}
]
},
{
"label": "us-east-1",
"unit": "%",
"points": [
{
"t": "2026-09-04T14:00:00Z",
"v": 0.3
},
{
"t": "2026-09-04T14:15:00Z",
"v": 0.3
},
{
"t": "2026-09-04T14:30:00Z",
"v": 0.4
},
{
"t": "2026-09-04T14:35:00Z",
"v": 0.4
},
{
"t": "2026-09-04T14:45:00Z",
"v": 0.4
}
]
}
],
"thresholds": [
{
"value": 5,
"label": "alert · 5%",
"tone": "warning"
}
],
"markers": [
{
"t": "2026-09-04T14:32:00Z",
"label": "deploy #4127",
"kind": "deploy"
}
]
}Table table
Ranked rows with typed columns: status codes as pills, shares as bars, numbers aligned.
- top error URLs
- slowest endpoints
- hosts by error count
- targets and their health
- anything ranked
Can be backed by a signed query that re-reads live. Follows the canvas time window.

Data shape
{ columns: [{ key, label, type: "string"|"number"|"time"|"status"|"share" }], rows: [{ [key]: string | number | { v, tone? } }], highlightKey? }Example
{
"columns": [
{
"key": "path",
"label": "Path",
"type": "string"
},
{
"key": "code",
"label": "Code",
"type": "status"
},
{
"key": "count",
"label": "Count 5m",
"type": "number"
},
{
"key": "share",
"label": "Share",
"type": "share"
}
],
"rows": [
{
"path": "/api/checkout/session",
"code": 502,
"count": 4812,
"share": 18.2
},
{
"path": "/api/cart",
"code": 502,
"count": 3109,
"share": 14.7
}
]
}Log view logView
Raw log lines in the window, colored by level, with timestamps.
- origin error lines
- the first errors after a deploy
- a filtered stream from one service
Can be backed by a signed query that re-reads live. Follows the canvas time window.

Data shape
{ lines: [{ t?: ISO-8601, level?: "error"|"warn"|"info"|"debug", message, fields? }], truncated?: boolean }Example
{
"lines": [
{
"t": "2026-09-04T14:33:02Z",
"level": "error",
"message": "CloudFront origin 502 Bad Gateway path=/api/checkout edge=eu-west-1"
},
{
"t": "2026-09-04T14:33:05Z",
"level": "warn",
"message": "target i-0a1 draining (2/6 healthy)"
}
]
}Timeline timeline
Discrete events in time order, each with a kind badge; a correlated one is highlighted.
- recent deploys and config changes
- feature-flag flips
- alarms as they fired
- what changed before the incident
Can be backed by a signed query that re-reads live. Follows the canvas time window.

Data shape
{ events: [{ t: ISO-8601, label, kind?: "deploy"|"config"|"flag"|"alarm"|string, detail?, correlated?: number|boolean, tone? }] }Example
{
"events": [
{
"t": "2026-09-04T14:32:00Z",
"label": "web-edge #4127 · task def :47",
"kind": "deploy",
"correlated": 0.94
},
{
"t": "2026-09-04T14:18:00Z",
"label": "checkout-v2 rollout 25% → 50%",
"kind": "flag"
}
]
}World map geo
Regions or points of presence on a world map, colored by status, to show WHERE an incident is.
- error rate by region
- edge locations affected
- latency by point of presence
- which regions are healthy
- any value keyed by a place
Can be backed by a signed query that re-reads live. Follows the canvas time window. Up to 60 series; the rest fold into one "other".

Data shape
{ points: [{ place: "eu-west-1"|"DUB"|"Frankfurt"|"Brazil"|…, label?, lat?, lon?, value?, unit?, tone?, pulse?, detail? }], unit?, legend? } — coordinates are filled from the place name when omittedExample
{
"unit": "%",
"points": [
{
"place": "eu-west-1",
"value": 12.4,
"tone": "critical",
"pulse": true,
"label": "eu-west-1 · 12.4% 5xx"
},
{
"place": "eu-central-1",
"value": 3.2,
"tone": "warning"
},
{
"place": "us-east-1",
"value": 0.4,
"tone": "good",
"label": "us-east-1 · 0.4%"
},
{
"place": "ap-southeast-1",
"value": 0.3,
"tone": "good"
}
]
}Code finding codeFinding
A code snippet pinned to a commit, with a permalink, captured when the finding was made.
- the exact lines a deploy changed
- a suspicious configuration value in source
- evidence read from a repository
Data only: captured once, never re-read.

Data shape
{ codeRef: { owner, repo, ref, path?, lineStart?, lineEnd?, pullRequestNumber?, permalink }, snippet: { lines: string[], startLine } }Example
{
"codeRef": {
"provider": "github",
"owner": "acme",
"repo": "web-edge",
"ref": "9f3c1e2a",
"path": "src/origin.ts",
"lineStart": 41,
"lineEnd": 44,
"permalink": "https://github.com/acme/web-edge/blob/9f3c1e2a/src/origin.ts#L41-L44"
},
"snippet": {
"startLine": 41,
"lines": [
"const ttl = 120;",
"export const origin = alb(\"web-edge-tg\", { ttl });"
]
}
}Time windows

One control row above the cards scopes every query-backed widget. The default is the incident window: from 30 minutes before the incident opened until now. A window can be at most 7 days; reversed or future ranges are refused with the reason.
- Incident window (fixed start, slides while live)
- Last 15 min (15m ending now, slides while live)
- Last 1 hour (1h ending now, slides while live)
- Last 3 hours (3h ending now, slides while live)
- Last 6 hours (6h ending now, slides while live)
- Last 24 hours (24h ending now, slides while live)
- Custom: an absolute start and end.
An agent may give a widget its own default window (for example the five minutes it looked at); the card names it in its head and the canvas control overrides it while selected.
Live, snapshot, stale
Each card carries exactly one freshness badge, and it never claims more than it knows.
- building: the agent requested the widget and the build is still running.
- live · updated Ns ago: the widget is in the refresh cycle and its last read succeeded. Live refreshes every 5, 15, 30 or 60 seconds, in one request for the whole canvas, and can be turned off.
- stale · last good: the last refresh failed; the card keeps the data it last had and says why.
- snapshot · HH:MM: data-only widgets (shared by an edge agent, the investigation status, a code finding) captured at that time and never re-read. With Live off, every query-backed card is a snapshot of its last read.
- no data in this window: the source answered with no points for the selected window; try the incident window.
- failed: the read failed and nothing good was ever shown for this window; the reason is on the card.
While a refresh is in flight the previous render stays visible, dimmed; a refresh button on each query-backed card and on the toolbar re-reads immediately. Widgets survive a server restart: the durable record carries everything needed to rebuild them.
Agents
The investigator agent puts a widget on the canvas with the same source, operation, parameters and connection it read with, plus the window it looked at and a grouping hint when values split by region or zone. It picks the type from this catalog, which it can ask for at any time with describe_widget_types. Your own coding agent, connected through the bridge, shares data-only widgets of any type here with share_with_room; the bridge exposes the same catalog. The world map places regions, edge locations, cities and countries by name, so a widget only has to say eu-west-1 or DUB.