The Run Dashboard¶
A study's state lives in files, and files are hard to watch. The bundled
dashboard is a local, read-only web view over studies/<id>/ that
compresses the workflow down to what you actually want to see: which stage is
done, what artifact it produced, the metric curve as it fills in, and each
agent's trajectory.
It has zero third-party dependencies — it runs on the same Python you
installed SocioVerse with, no pip install. The data tables read the DuckDB
stores, so they need duckdb in that interpreter, which the SocioVerse install
already provides; without it only those tables stay empty.
Start it¶
--open launches your browser. In practice you rarely type this: the agentic
workflow starts the dashboard for you — /sv-init launches it as its first
action, opens a tab, and prints the URL; every later sv-* stage reuses the
same server silently.
One server per workspace
Launching no-ops if 8787 is already up, so several Claude Code sessions
on the same workspace share one dashboard instead of fighting over the
port. Idle self-reap is off by default — once launched the server
holds the port for the whole session, so a backgrounded tab or a paused
run never loses it. Set SV_DASH_IDLE=<seconds> if you'd rather it quit
when nothing is happening.
| flag | default | effect |
|---|---|---|
--port |
8787 |
port to bind |
--host |
127.0.0.1 |
bind address; use 0.0.0.0 to serve from inside a container |
--root |
the repo's studies/ |
point the view at a different studies directory |
--open |
off | open a browser tab on start |
| env var | default | effect |
|---|---|---|
SV_DASH_PORT |
8787 |
port the workflow's hooks talk to |
SV_DASH_OPEN |
1 |
0 = announce the URL but don't open a browser |
SV_DASH_IDLE |
0 (off) |
seconds of idleness before self-shutdown |
SV_DASH_DISABLE |
— | set to anything = the workflow never launches or announces it |
SV_DASH_SHOW_TEMPLATES |
— | 1 = also list the bare abm_* template shells |
What's on screen¶
┌ top bar ── title · path · research question · run status · study switcher ─┐
├ pipeline ─┬─ active-stage detail ──────────────────────────────────────────┤
│ sv-init ✓ │ per-stage card (init facts / env layers / run config / report)│
│ …model – │ │
│ …env ✓ │ │
│ …pop ✓ ├─ metric timeline ───────────────┬─ figure carousel ───────────┤
│ …run ✓ │ lines + intervention markers │ reports/figures/*.png │
│ …report ◔ ├─ event feed ────────────────────┴─────────────────────────────┤
└───────────┴─ compressed, semantic-only (no raw tool spam) ─────────────────┘
- Pipeline rail — one chip per
sv-*stage with its status (see the vocabulary below). Clicking a stage opens its detail card. - Stage detail — the artifact that stage wrote, rendered:
sv-init's grounded facts, the environment's layers and scheduled interventions, the population's composition, the run config (includingtime_unit/step_meaning), the report. Each card also carries a short Reasoning note the skill self-reported when it ran. - Metric timeline — every declared metric as a line, with markers where
interventions fired. Legend entries carry each metric's
metric_descriptionstext as a tooltip; clicking one hides that series. - Figure carousel — the PNGs under
reports/figures/. - Agents card — every instantiated agent, and for the selected one its per-step state, decision, and output. This is the panel data made visible: an agent's trajectory is its memory.
- Event feed — a log of what happened, reconstructed in full on every request, so earlier lines never vanish when a later iteration re-runs a stage.
- Versions & branches — the
/sv-iteratetree of research states. Every node carries a kind badge: version (a cold re-run) or branch (only new interventions, its history before the fork step replayed from the parent — "branched from v1 · at step 5"). A branch whose workspace edits went beyond the environment carries an amber downgraded to version chip: it ran cold. Selecting an archived node renders it read-only from its snapshot.
Status vocabulary¶
The colours mean specific things, and the distinctions are the point:
| badge | meaning |
|---|---|
| done | this stage's artifact is current |
| stale (coral, "re-run") | the artifact predates a build artifact it depends on — something upstream changed under it |
| inherited (amber) | a Path-A fork copied this artifact from its source and you haven't re-authored it yet |
| from v\<parent> (neutral) | a version deliberately reuses its parent's artifact — reuse is intended, not a warning |
| archived (purple) | you are viewing a frozen versions/vN/ snapshot, not the live directory |
Live during a run¶
The dashboard streams while /sv-run is working. The engine appends
trajectory/progress.jsonl each step, and a lock-free
trajectory/panel_live.jsonl mirrors the panel rows (DuckDB is write-locked
mid-run) — so the step bar advances, the chart grows, and the agent inspector
updates before the run finishes. The browser reconnects over SSE by itself if
the server restarts.
A top banner and the tab title (⏳ waiting for you) flip on when the
workflow is blocked on your answer, so a backgrounded tab still signals.
Compare versions and branches¶
The metric timeline card's title row has a vs selector. Picking a baseline
overlays it on the timeline as dashed lines in the same hue, draws each node's
intervention markers separately, and shows a Δ-config strip: what changed in
n_steps / seed, which scheduled events and broadcasts were added or
removed, and how the persona count differs. It affects only that card — the
rest of the view stays on your current version.
When the baseline is the version a branch came from, the pair is labelled control (the source) and treatment (the branch): the two share an identical history up to the fork step, so the distance between the curves is the declared intervention's effect. Any other pair compares two research designs, which is what the Δ-config strip is for.
Where the data comes from¶
Two channels, deliberately separated:
- Files are authoritative. The server watches
studies/<id>/and reconstructs everything from the artifacts the skills already write —study.yaml,environment.json,population.json,simulation.json,trajectory/progress.jsonl,metrics_history.json,reports/,versions.json. That makes the view robust to Claude Code restarts, subprocess writes, and hand-edits. POST /ingestcarries reasoning. Each stage self-reports a one-line narrative, merged onto its card and into the feed. Nothing else is pushed — there is no raw tool-call stream.
The HTTP surface is small and stable enough to script against:
| route | purpose |
|---|---|
GET /api/state |
study list + progress + current selection |
GET /api/study/<id> |
full reconstructed detail for one study |
GET /api/study/<id>/agents · …/agent/<aid> |
the agent inspector |
GET /api/study/<id>/version/<v> |
an archived version's detail (…/agents, …/figure/<name> too) |
GET /api/stream |
SSE nudge on any artifact write |
POST /ingest |
record a stage narrative |
curl -X POST http://127.0.0.1:8787/ingest -H 'Content-Type: application/json' \
-d '{"study_id":"chicago_schelling","stage":"sv-init",
"narrative":"Routed to Path A — matched on domain=urban-segregation; forked the baseline."}'
Read-only, by design
The dashboard never writes to a study. It cannot start a run, edit an artifact, or delete a version — those are workflow actions. The one exception is the hosted service, where a chat panel appears next to the same view; see the hosted workbench.
Export a static copy¶
To share results without shipping the server (a paper appendix, a project page), export a self-contained snapshot:
python scripts/export_dashboard_static.py \
--studies opinion_diffusion consumer_confidence chicago_schelling \
--out ./gallery
The output is plain HTML + JSON with deep links (index.html?study=<id>) and
needs nothing but a static file server.