Dashboard
When the broker is enabled, one tmux pane runs the dashboard instead of an agent CLI, and it is pane 0 in every mode: the full-width top row above the agent grid, with the coding agents laid out row-major beneath it (no orchestrator CLI runs inside the session). That is the same layout in a supervisor-mode session, a bare one and a spec-driven one — every session git-paw builds uses the agent grid. It renders the same live status table that refreshes about once a second (and immediately on a key press), giving you an at-a-glance view of what each agent is doing. A supervised session builds the dashboard pane even with the broker off.
The dashboard is observation-only — it never sends actions back to agents, and no git-paw keystroke sender ever types into it. Beyond q to quit, its keystrokes drive the Broker log panel (toggle, filter, inspect). Human input (questions, directives, replies to agent.question events) happens in the orchestrator’s own agent session, outside tmux; the dashboard simply renders broker state. There is no inbox panel.
What the Dashboard Pane Shows
The dashboard renders a table with one row per agent. Once the orchestrator has published its own agent.status (as supervisor), its row is pinned to the top with a horizontal-line divider beneath it; coding-agent rows follow in alphabetical order:
┌────────────┬────────┬────────────┬─────────────┐
│ Agent │ CLI │ Status │ Last Update │
├────────────┼────────┼────────────┼─────────────┤
│ supervisor │ claude │ 🔵 watching│ 12s ago │
│ ────────── │ ────── │ ────────── │ ─────────── │
│ feat-api │ claude │ 🟡 blocked │ 1m ago │
│ feat-auth │ claude │ 🔵 working │ 3m ago │
│ fix-typo │ codex │ 🟢 done │ 8m ago │
└────────────┴────────┴────────────┴─────────────┘
When no orchestrator has published (e.g. broker-only mode — broker on, no --supervisor — or a supervised session before your orchestrator session has picked up its context), the divider is not rendered and coding agents fill rows alphabetically from the top.
Status Symbols
| Symbol | Meaning |
|---|---|
| 🔵 | Working – agent is actively processing |
| 🟢 | Done/verified – agent has completed its task |
| 🟣 | Committed – agent has committed work |
| 🟡 | Blocked – agent is waiting on something |
| ⚪ | Idle / unknown phase – agent has not reported a recognised status |
The Last Update column shows elapsed time since the agent’s last status update (30s ago, 3m ago, 1h 15m ago).
Supervisor row, cli field, and phase field
The supervisor row’s Status column does not show the wire-message type label that a coding-agent row shows. Instead the supervisor publishes a phase field on its agent.status messages (e.g. baseline, watching, approving, answering, merging, summary), and the dashboard prefers that label when rendering its row. This avoids the misleading status=feedback label the supervisor would otherwise show when it publishes agent.feedback to a coding agent.
The orchestrator has no pane and no worktree, so it is not a watch target and the broker cannot infer its cli from the watch-target map. To populate the CLI column for the supervisor row, the supervisor self-registration agent.status includes a cli field (e.g. "cli":"claude"). The broker upserts that value into its internal CLI map when it receives the message.
The CLI column is populated for every agent row, not just the supervisor. git-paw knows each pane’s CLI at launch and pre-fills it authoritatively — coding agents from the watch-target map (the per-repo session JSON), the supervisor from [supervisor].cli/default_cli. Agents do not self-report their CLI (they would only be guessing). A row appears once its pane publishes a status, and its CLI column shows the pre-filled value; if a CLI somehow can’t be resolved it shows a ? placeholder rather than a blank cell.
A coding-agent row can also show stuck-on-prompt in its Status column: the bundled sweep.sh helper’s stuck detector publishes this phase directly on the stalled agent’s own agent.status when its pane shows a permission-prompt marker and its heartbeat has stopped advancing (see Agent permissions). This is the one coding-agent phase outside the generic working/done/blocked/committed set in Status Symbols above.
The roster holds only real agents
The /status agent roster — and therefore the dashboard table — is built only from agents that publish agent.status. The from/target/verified_by identity fields on agent.feedback, agent.question, and agent.verified messages are routed and stored but never mint a roster row. This means a human- or supervisor-originated feedback can no longer create a phantom human agent that never heartbeats. The roster is in-memory, so any pre-existing phantom from an older broker also clears on the next git paw start.
When the supervisor row appears
The supervisor row appears after the orchestrator — your own agent session, outside tmux — publishes its first agent.status as supervisor, its first action on picking up .git-paw/AGENTS.local.md. The launcher never publishes on its behalf, and the drive loop’s escalations (published as agent.question from supervisor) never create the row either, so a session no orchestrator drives shows no phantom supervisor row.
Stalled-wave indicator
Agent rows answer “is this agent alive?” The status line at the bottom of the dashboard answers the question they cannot: “is the wave getting anywhere?” Whenever the broker reports the wave as stalled, the status line carries an indicator naming how long it has been idle:
wave stalled 1h
The reading comes straight from the broker’s
wave staleness state, so the same two things
clear it that count as progress anywhere else — a wave phase advancing or a
branch tip moving. The indicator disappears on the first refresh that reports
stalled = false; nothing is shown while the wave is progressing. Tune (or
disable) the threshold with [broker] wave_stall_minutes — see the
configuration reference.
This is the dashboard’s view of a session that looks healthy row by row and is
not moving: every pane alive, every agent working, and nothing having
actually advanced since the unanswered prompt.
Controls
| Key | Action |
|---|---|
q | Quit the dashboard (shuts down the broker; agent panes keep running but lose broker communication) |
l | Toggle the Broker log panel on/off |
a | Reset the Broker log filter to All |
1–9, 0 | Toggle the individual filter chips (status / artifact / blocked / verified / feedback / question / intent / verify-now / advanced-main / learning) |
↑/k, ↓/j | Move the highlight up/down the Broker log rows |
Enter | Open the details overlay for the highlighted row |
Esc | Close the details overlay |
Broker log panel
The Broker log panel fills the screen region freed when v0.5.0 removed the
prompt inbox. It renders a scrolling, type-filterable list of the broker
messages observed during the current dashboard session, newest at the top, so
you can watch the session’s wire-level activity at a glance instead of tailing
/messages/<id> over curl.
The log is in-memory only — closing the dashboard drops it — and bounded by
[dashboard.broker_log] max_messages (default 500). Older messages fall off the
top as new ones arrive. The panel is read-only: replying to a question or
directing an agent happens in the orchestrator’s own session.
When visible, the panel occupies a fixed 20 rows by default — materially more
than its original 12 — so more coordination traffic is visible without constant
scrolling. Tune it with [dashboard.broker_log] height_lines. The agent-status
table keeps a positive minimum height and absorbs the terminal’s slack, so on a
tall terminal the table grows while the panel stays at its configured height; on
a terminal too short to grant both, the panel yields space before the table
shrinks below its minimum.
Showing and hiding the panel
The panel is visible by default. Toggle it with l, or set the launch default
and height in .git-paw/config.toml:
[dashboard.broker_log]
max_messages = 500
default_visible = true
height_lines = 20
When the panel is hidden the dashboard layout is identical to its v0.5.0
post-inbox-removal shape (title, agent table, status line) — the agent table
expands to fill the freed space, and the status line gains a
broker log hidden — press l to show hint. See the
configuration reference for the
table’s fields.
Filter chips
A header row of chips sits above the message list, one per broker message type
plus an All reset. All is active by default (every message shows). Pressing
a digit hotkey (1–9, then 0 for the tenth chip) narrows the view to that type; pressing more digits adds
types inclusively; pressing an active chip again removes it (emptying the
selection returns to All). Press a to reset to All at any time. Filtering
is a view operation — the ring buffer always retains every message regardless of
the active chips.
| Hotkey | Chip | Matches |
|---|---|---|
1 | status | agent.status |
2 | artifact | agent.artifact |
3 | blocked | agent.blocked |
4 | verified | agent.verified |
5 | feedback | agent.feedback |
6 | question | agent.question |
7 | intent | agent.intent |
8 | verify-now | supervisor.verify-now |
9 | advanced-main | agent.advanced-main |
0 | learning | agent.learning |
agent.answer messages have no chip of their own; they show in the default
All view.
Row format
Each row is a single line: HH:MM:SS · type · agent · summary, where the
summary is a per-type one-liner derived from the message body (the status
message, the first modified file of an artifact, the blocking need, the intent
summary, and so on). Summaries that overflow the panel width are truncated with
an ellipsis (…); the full body is available in the details overlay.
┌─ Broker log (7 shown / 7 held) — l hide · a all · 1-9·0 filter · ↵ details · Esc close ─┐
│ All 1:status 2:artifact 3:blocked 4:verified 5:feedback 6:question 7:intent 8:verify-now 9:advanced-main 0:learning │
│ 14:35:09 · status · feat-auth · working: rebasing onto main │
│ 14:34:58 · intent · feat-auth · wire AuthClient │
│ 14:34:12 · blocked · feat-api · needs auth token format from feat-auth │
│ 14:33:40 · artifact · fix-typo · done: src/typos.rs │
└────────────────────────────────────────────────────────────────────────────────────────┘
Status activity phrases
An agent.status payload may carry an optional activity field — a short
phrase naming what the agent is doing right now. When present it is appended to
the status summary in parentheses, so a heartbeat conveys the work rather than
just a bare working:
│ 14:35:09 · status · feat-auth · working (running the test suite) │
The field is optional and additive. A status that omits it renders exactly as it did before the field existed, and agents are never required to set one.
Heartbeat collapsing
Agents heartbeat often, and a wall of identical working lines buries the real
state transitions between them. The panel therefore collapses a run of
consecutive status rows that are identical in every rendered field — same
agent, status, activity, and message — into one row carrying the run’s latest
timestamp and a ×N repeat count:
│ 14:35:09 · status · feat-auth · working ×12 │
│ 14:33:40 · artifact · fix-typo · done: src/typos.rs │
Anything that differs breaks the run and starts a new row — a changed status, a
changed activity phrase, a different message, another agent, or any non-status
message arriving in between. So a real transition such as working → verified
always gets its own row, and two distinct working phases separated by other
traffic are never merged.
Like filtering, the collapse is a view operation: the ring buffer still retains every individual message, the count in the panel title reflects the rows drawn, and opening the details overlay on a collapsed row shows the newest message of that run.
Details overlay
Highlight a row with the arrow keys (or j/k) and press Enter to open a
modal overlay showing that message’s full, pretty-printed JSON in a scrollable
view. Press Esc to close it and return to the panel. While the overlay is
open, q still quits the dashboard.
Resilience across broker restarts
The panel never clears its buffer when the broker is unreachable, paused or restarted. Historical messages stay visible across the outage, and new messages resume appearing at the top once the broker answers again. A restarted broker host numbers its log from 1 again; the dashboard notices from the host’s uptime going down, re-reads the new log from the start, and appends each new entry once — nothing it already shows is duplicated. A gap in the timestamp column is the only visible sign that messages briefly stopped flowing.
Legacy messages panel
The earlier [dashboard] show_message_log flag rendered a simpler, unfiltered
messages list. It is superseded by the Broker log panel and retained only for
config compatibility.
Relationship to the Broker
The dashboard is a thin client of the broker. The broker runs in its own process, git paw __broker, in a detached tmux window named broker (outside the pane grid, so pane numbering is unchanged; switch to it to see the host’s stderr). The dashboard (git paw __dashboard, pane 0) holds no broker state: it reads the broker URL from the saved session and polls GET /status and GET /log?since=<seq> on its idle cadence. Closing the dashboard pane has no effect on the broker, its watchers, conflict detection or learnings.
When the broker is unreachable
If a poll cannot reach the broker — git paw pause stopped it, the host is restarting, or a fresh launch has not finished bringing it up — the status line shows ⚠ broker unreachable, the agent table keeps the last snapshot, and the dashboard keeps polling. The indicator is independent of the stalled-wave indicator, and it clears on the first successful poll. An unreachable broker never makes the dashboard exit.
Lifecycle and exit conditions
The dashboard process is bound to its session: when the session ends, the dashboard exits on its own rather than lingering and busy-rendering to a dead terminal (a leaked __dashboard process pegging CPU). Its draw loop checks a single lifecycle gate on every iteration — including any error or degraded path, so no branch can bypass it — and exits when any of the following holds:
- Clean shutdown. tmux delivers
SIGHUPwhen it kills the session or pane, which ends the process. - Orphaned to init. On Unix, if the parent process is gone the dashboard has been reparented to init (
getppid() == 1) — the tell-tale of a session torn down withoutSIGHUP(an abrupttmux kill-server, a crash, or the machine sleeping). The loop notices the reparent and exits. On non-Unix platforms this check is inert and only theSIGHUPshutdown path applies. - Controlling terminal gone. If the pane’s terminal hangs up, reading input (
event::poll) errors, or a write to the terminal fails, the dashboard treats its controlling terminal as gone and exits. This catches the case where the pane was killed but the dashboard was reparented to a lingering shell (a live but unrelated parent), so the orphan-to-init check alone would miss it.
An unreachable broker is deliberately not on this list (see above). The broker host has its own lifecycle: it flushes the broker log and exits on SIGHUP (when pause closes its window or stop kills the session), when orphaned to init, or when its terminal hangs up, and it exits non-zero with a diagnostic naming the port if it cannot bind — it never retries in a loop.
Because the dashboard is cheap to relaunch (git paw start re-attaches to the session), exiting eagerly on any of these signals is preferred over risking a leaked, busy-looping process.
Replying to agent questions
Earlier dashboard versions included a “Questions” panel and a “Reply to” input field for human-typed answers to agent.question events. The panel was removed in v0.5.0; the dashboard has no inbox. Questions and replies go through the orchestrator — your own agent session, outside tmux — and its helpers, not through the dashboard.
agent.question messages still flow through the broker. The orchestrator reads incoming questions from the broker (through sweep.sh, the broker helper or the MCP server) and replies with sweep.sh answer (an agent.answer on the broker) plus tmux send-keys to the asking agent’s pane. See the Orchestrator mode chapter and the exported orchestrator skill (.git-paw/skills/orchestrator/SKILL.md) for the reply flow.
See the v0.5.0 changelog for the removal note.