ADR-0015: Runs List Design — Identity, Filters, Pagination¶
Status: Accepted Date: 2026-05-20 Extends: ADR-0012 (execution lineage), ADR-0004 (data authority)
Context¶
The runs list page (/runs) shows 376 SQLite sessions in a flat table with minimal identity: name (often just "flow" or "agent"), branch count, message count, status (almost all "completed"), and timestamps. With enriched session provenance (ADR-0012: playbook_name, agent_name, invocation_kind, show_topic, show_play_name, source_kind), the list can become an operational execution index.
The page currently has no search, no filters, no pagination, and no way to distinguish 376 rows that mostly share the same name and status.
Decision¶
1. Two-line row with display name + meta line¶
Each row has a primary display name (computed from provenance) and a secondary meta line (session ID + supplementary context):
unimpl-adr-sweep / closeout-audit show play completed 1 br · 24 msg 15h ago
session 20260520T0A7... · playbook show · agent orchestrator
flow flow completed 6 br · 632 msg 1:33 PM
session 20260520T015... · started May 19 11:06 PM
Display-name algorithm¶
Fallthrough from most-specific to least-specific:
if (show_topic && show_play_name) → `${show_topic} / ${show_play_name}`
else if (playbook_name) → playbook_name
else if (agent_name) → agent_name
else if (name) → name
else → shortSessionId
Meta line: session ID + any provenance not already in the display name + start time. No "unlinked" or "unavailable" labels — if provenance is absent, the row simply falls back to session ID and timestamp.
2. Default columns¶
| Column | Width | Default | Content |
|---|---|---|---|
| Run / Origin | flex:1, min 420px | Visible | Display name + meta line |
| Kind | 110px | Visible | agent, play, flow, fanout, show play |
| Status | 120px | Visible | Normalized display status (ADR-0012 mapping) |
| Activity | 140px | Visible | N br · M msg |
| Updated | 150px | Visible | Relative timestamp, primary sort |
Hidden columns (available via [Columns] toggle):
- Started, Source kind, Playbook, Agent, Show topic, Show play, Session ID, Branches (split), Messages (split)
3. Filter bar¶
Dense two-row filter bar above the table:
[ Search name, id, show, play, playbook, agent... ] [Columns ▾]
Status: [All] [Running] [Completed] [Failed] [Aborted]
Kind: [All] [Agent] [Play] [Flow] [Fanout] [Show play]
Source: [All] [Live] [Imported fs] [With show] [With playbook] Rows: [100 ▾]
Search matches: session.id, session.name, playbook_name, agent_name, invocation_kind, show_topic, show_play_name, source_kind. Client-side filtering on the loaded session list (no server-side search endpoint needed at 376 rows).
4. Pagination at 100 rows/page¶
| Row count | Strategy |
|---|---|
| 0–200 | Render all, no pagination needed |
| 200–2,000 | Paginate, default 100/page |
| 2,000+ | Server-side pagination (add LIMIT/OFFSET to sessions query) |
No virtual scroll. It complicates row heights, browser find, copy workflows, keyboard navigation, and future expandable rows. Pagination is simpler.
5. Empty provenance handling¶
During the transition period (most sessions have null provenance):
- Rows with provenance get descriptive display names (show/play, playbook, agent)
- Rows without provenance fall back to session name + timestamp
- No "unlinked" labels, no "missing" indicators — absence is handled by graceful fallback, not by calling attention to what's missing
- As new sessions are created with provenance, they naturally stand out
Consequences¶
Positive
- Every row is identifiable even when 100+ share the same session name.
- Filters enable targeted diagnosis (show me all failed show-plays).
- Pagination prevents render performance degradation.
- Column toggle lets power users expose raw metadata when needed.
Negative
- Two-line rows use more vertical space per row (~52px vs ~36px).
- Display-name algorithm requires client-side computation per row.
- Filter state is local with one exception: the
?status=query parameter is supported for dashboard deep links (ADR-0012). Other filter dimensions (kind, source, search) are local-only until deep-linking is needed.
Alternatives Considered¶
| Alternative | Why Rejected |
|---|---|
| Virtual scroll instead of pagination | Complicates browser find, copy, keyboard nav; pagination is simpler |
| Per-row error count column | Requires loading all messages for every session in the list query; too expensive |
| "Unlinked" labels for missing provenance | Visual noise on 376 rows that ALL lack provenance; trains users to ignore the block |
| Sidebar filters | Takes horizontal space from the table; above-table filter bar is denser |
| Server-side search | Not needed at 376 rows; client-side filtering is sufficient |