dashboard · git:20261105.0d95dc2 · 2026-11-05 · sha256 58b3d437df9576fe

dashboard git:20261105.0d95dc2A

Immutable. This exact content never changes and is served at /api/v1/blob/58b3d437df9576fe.

---
name: dashboard
description: Watch Arbor campaigns in a browser. Local mode shows one campaign live, collected over ssh where its agents run; global mode shows every campaign on the shared volume by project, user and campaign, with each one's last snapshot and what it learned. Use when asked to watch, monitor or show a campaign or all campaigns, to see a campaign's phase, iteration and verdicts at a glance, or to browse what other campaigns pushed.
---

# Dashboard: one campaign live, or every campaign on the share

Audience: whoever runs or follows Arbor campaigns. The server is
[`arbor/dashboard/server.mjs`](../../../arbor/dashboard/server.mjs) (Node 16 or
later, no dependencies); its README has the files, routes, what changed from
the earlier dashboard it was ported from, and troubleshooting. The data comes from
the collectors (`realtime-collector`, `knowledge-collector` skills).

The server runs on your machine, binds to `127.0.0.1` only, and runs
read-only commands over ssh with `BatchMode`. It never writes on a host or the
share, and never samples a GPU node.

## Which mode

| You want | Mode |
| --- | --- |
| every campaign, anyone's, by project; a campaign whose host you cannot reach; what a campaign learned | global |
| your campaign, live, straight from its host (before its realtime collector is set up, or when the share is down) | local |

## Global mode: everything on the share

```bash
node arbor/dashboard/server.mjs global          # http://localhost:8767
```

- Needs the `arbor-share` ssh alias (the `realtime-collector` skill, "Once per
  person"). `--share-host`, `--share-root` (or `ARBOR_SHARE_HOST`,
  `ARBOR_SHARE_ROOT`) point it elsewhere, such as a scratch root under `/tmp`.
- **The index** (`/`) lists every campaign under `<root>/projects/`, grouped by
  project: user, state, the iteration in flight, baseline and best with the
  gain, keep and revert counts, when it was last updated and from which host,
  and when its knowledge was collected. Filter by project, user and state;
  the filters stay in the URL, so a filtered view can be shared as a link.
  It refreshes every 30 s (`--refresh`); each refresh is one ssh command.
- **States**: `live` (a snapshot from the last 15 minutes, with no sign the
  campaign stopped); `quiet` (no snapshot for 15 minutes: the collector stopped, or
  the campaign ended without saying so); `stopped` (the log names the end);
  `not running` (no driver on the campaign's host and no end named: it may
  have been killed); `aborted` (an `ABORTED.md`); `knowledge only`.
- **A campaign** (`/c/<project>/<user>/<campaign>/`) is the campaign page
  below, from the snapshot its collector last pushed, plus a knowledge panel:
  the summary, outcomes, findings, each profile's report and the dossier's
  index, and every file on the share, each a link that fetches it. It is
  fetched while the page is open, at most once per refresh interval.

## Local mode: one campaign, from where it runs

```bash
node arbor/dashboard/server.mjs local --host <host with the session> \
    --session <session dir> --config <config> --log <campaign stdout log> \
    [--slurm-job <job name>] [--results <dir>] [--events <file>] [--label <tab name>]   # http://localhost:8766
```

- Each refresh (every 15 s; `--refresh`) sends `arbor/collect/realtime.py`
  from this checkout inside one ssh command and runs it there. The host needs
  only `python3` and the campaign's files; no Arbor checkout there, and
  nothing someone changes in one, affects the page. It writes nothing, so it
  can run beside the campaign's realtime collector. Paths are absolute.
- **A driver inside a Slurm job** (a chain of chunks on GPU nodes) is
  invisible from the login node: give `--slurm-job <job name>`, and the job's
  state decides running, between chunks (only a queued successor) or unknown
  (`squeue` failed). Without it, the page looks for an `arbor run` process on
  the host and reports a chained campaign as not running.
- `--host` is an ssh alias with key authentication. `--host local` runs the
  collector on this machine (it needs `python3` here: WSL, not Windows).
- **Run it from WSL with a reused connection** when the host is slow to reach
  (`<login node>`: seconds per ssh connection): `--ssh-opts "-o ControlMaster=auto
  -o ControlPath=$HOME/.ssh/cm-%r@%h:%p -o ControlPersist=10m"`. Windows'
  OpenSSH cannot reuse connections. On Windows the server uses
  `C:\Windows\System32\OpenSSH\ssh.exe` when present, because Git for Windows'
  ssh cuts a command at about 8 KB. The Model M campaigns' pair runs this way:
  `scripts/model-m-campaign/dashboards.sh start|stop|status`.
- On a Cluster B login node, use one that accepts remote commands (`<login node>`)
  and keep the refresh at 15 s or more: every refresh is a
  process on a host with a 4 GiB per-user cap, and when the host refuses a
  session, the page keeps the last snapshot and says so.
- Several campaigns: one server each, with its own `--port` and `--label`.

## The campaign page

- **Top bar**: the phase chip (the iteration in flight and what it is doing,
  from the log's last marker: agent working, warmup, measured replay, critic,
  gate), and two clocks: when the snapshot was taken, and when the campaign
  last wrote its log. A stale snapshot is never shown as live.
- **Banners**: a snapshot over 15 minutes old (global); no driver running; a
  STOP, CHAIN_STOP or `ABORTED.md` file; supervisor restarts; a collector
  that cannot reach the share; a refresh that failed (the last good snapshot
  stays, labelled).
- **Cards**: baseline, best and gain, the iteration (in flight vs. last
  booked), keep and revert counts. **Progress** shows only when the config
  states a target.
- **History**: every booked iteration, newest first, with its verdict, metric
  and change. Then baselines (with `--results`), A/B (when present), events
  (with `--events`), the agent's latest tool calls, the log's tail, and the
  collector's own errors.

## Keep it running

In a terminal is enough. To keep one up in WSL as a user unit:

```bash
systemd-run --user --collect --unit=arbor-dash-global --setenv=PATH="$PATH" \
  /bin/bash -lc "exec node <Arbor checkout>/arbor/dashboard/server.mjs global >> ~/arbor-dash-global.log 2>&1"
```

WSL forwards `localhost`, so the Windows browser opens it. Where dashboards
are collected in a hub, link it
there instead of passing the port around:
`hub-register --link <id> http://localhost:8767 "Arbor share"`.