Design: session hub — live session dashboard + wrapup session report

Requirement jwildfire/obot.roadmap#24 · 2026-07-11 · Decisions resolved 2026-07-11 — D1–D4 approved, all recommendations accepted
The driver requirement for obot.agent v0.2. Visual spec: concept mockup (built 2026-07-11 from that morning's real session data).

1. Context and goals

obot.agent v0.1.0 (released 2026-07-11) gave working sessions a full lifecycle — session-init opens, five skills capture state along the way, session-wrapup closes into a diary entry — and the lead/sibling spawn pattern made sessions multi-agent operations: the 07-11 sessions ran four to five concurrent named agents. What v0.1 did not ship is a way to see a session. Mid-session state lives in files designed for agents (scratchpad, state.json, memory), and the operational picture at wrapup is reconstructed from transcripts and then discarded.

#24 closes that gap with a read-only static generator that renders one self-contained HTML page from the sources the session framework already maintains — a live dashboard during the session, and at wrapup the same view frozen into a session report published on the hub and linked from the diary entry. The reports double as keynote material for the R/Pharma 2026 diary narrative (#22, #10). Goals, in priority order:

2. Where the data already lives — verified inventory

Every input was verified against live data on 2026-07-11. Pinned fields are the parse contract (§4).

SourceWhat it providesPinned fields / format
~/.claude/jobs/<id>/state.json Per background session: identity, status, progress, links, output name, color, state (working / needs‑input / done / failed), detail, tempo, tokens, children[] (PR/issue links: kind, href, id), output.result, createdAt, firstTerminalAt, updatedAt, cwd, respawnFlags (model), intent (briefing text)
claude agents --json [--all] --cwd <ws> Session index: interactive sessions (which have no state.json) + liveness of background ones kind (interactive/background), name, status (busy/idle), state, startedAt, sessionId, id, cwd
Scratchpad .claude/session-notes/YYYY-MM-DD.md Priorities with check-state, mid-session todos, notes, scaffold candidates, per-agent session logs Sections ## Overview (session-init; grouped checklists + <!-- session-init … --> marker), ## Todo, ## Notes, ## Scaffold (new, §7); ## Session log — … headings
Memory + diary “Next session” panel memory/next-session-todo.md body; newest diary/*.md “Next session: loose ends” section
GitHub (batched gh sweep) Roadmap activity since session start gh search issues/prs --owner jwildfire --updated ">=<start>" + project-board statuses (obot Roadmap, user project 1). Derived and cached, never stored.

Not available (and therefore out of scope for v1, §9): per-session cost in USD. The statusline stdin JSON carries cost.total_cost_usd, but it is push-based, render-time-only, and interactive-only — there is no persisted per-session cost store. state.json → tokens is exact and persisted, so tokens are the v1 effort metric; a cost estimate (tokens × model rates, model from respawnFlags) is a cheap follow-on once wanted.

3. Prior art absorbed: the session-orchestration proposal

The session-orchestration proposal (2026-07-04) designed the same territory before the session skills existed, and left four decisions open. Events resolved most of it; #24 absorbs the remainder. Disposition, decision by decision:

2026-07-04 decisionDisposition
D1 — track as a new hub Requirement, or fold into #18? Overtaken The lifecycle/skills half shipped with obot.agent v0.1 (session skills absorbed via obot.agent#13 / hub #23); the spawn pattern is live practice. The visibility/tracking half is #24 — this requirement.
D2 — who answers a lane's needs input:? Not absorbed Deliberately out of scope here (§9). The dashboard surfaces needs-input prominently (banner + agent pill); answering stays in the agents view or the lead session. The relay question moves to #18's design.
D3 — standing watcher or on-demand status? Superseded by #24 The live dashboard is the standing watcher — a page humans glance at, replacing both the 30-second poll loop and “ask an agent to re-render the todo list.” One-shot generation still works for on-demand use.
D4 — one skill or two? Superseded v0.1 shipped five session skills; the wrapup/diary procedure the old D4 wanted separated exists as session-wrapup.

Applied with sign-off (2026-07-11): the 2026-07-04 report now carries a status note (“absorbed by #24”) at the top of its README and page — annotated, per hub convention, never deleted. That closes the “session-orchestration D1–D4” item that had ridden the decisions backlog since 07-04.

4. Architecture — one generator, three collectors, two modes

A single zero-dependency Node script, tools/session-hub/session-hub.mjs, shipped in obot.agent (the versioned home of the session skills it visualizes; same layout precedent as the hub's scripts/render_diary.mjs). No server, no npm installs, no runtime state of its own.

collect                       normalize                render
───────                       ─────────                ──────
jobs/*/state.json   ─┐
claude agents --json ┼──►  session model (JS object) ──►  one self-contained HTML file
scratchpad sections  ┤     · session identity + boundary      · live:   .claude/session-hub/live.html
memory + last diary  ┤     · agents[] (pinned fields)         · report: obot.roadmap/reports/sessions/
gh sweep (cached)   ─┘     · priorities/todos/notes/scaffold            YYYY-MM-DD[-N].html
                           · roadmap events since start

5. Panels — the render contract

Exactly the mockup's inventory; each panel header names its source on the page itself, so the dashboard doubles as documentation.

PanelSourceNotes
Masthead + mode pillgenerator argsSession name/date · workspace · Live/Report · generated-at stamp
Alert banneragents modelOnly when ≥1 agent is needs-input/failed (or a rate-limit pattern is detected in detail); otherwise absent
Stat tiles ×4derivedPriorities open/done · agents by state · total tokens · roadmap-event count
Prioritiesscratchpad ## OverviewGroups preserved (agent-actionable / waiting on @jwildfire / parked); check-state + issue refchips linkified
Roadmap activitygh sweep (cached)Time-ordered events: PRs opened/merged, issues filed/closed, board moves, releases — since session start
Agentsstate.json + claude agents --jsonPer session: color dot (orange lead / green sibling convention), name, state pill, one-line detail, since/duration, token bar, model, PR/issue chips from children; output.result when done
Notesscratchpad ## NotesTimestamped, newest last
Scaffold improvementsscratchpad ## ScaffoldThe one new capture channel (§7)
Next sessionnext-session-todo memory + last diary loose endsRender-only

6. Report mode and the diary

At wrapup, session-hub.mjs --report renders the final state with report semantics: the mode pill flips, the banner becomes an outcome strip, agents show final results (output.result), durations and total tokens, priorities show final check-state, and roadmap activity becomes the session's definitive event timeline. Output: obot.roadmap/reports/sessions/{slug}.html, where the slug mirrors the diary entry (2026-07-11, 2026-07-11-2, …) — one report per diary entry, forever paired.

Diary integration (D2) is link-first: the wrapup commits the report and adds one line to the diary entry (“📊 Session report” under the meta block). That needs zero changes to render_diary.mjs — a Markdown link renders as-is. Promoting the report to a proper tab on the diary page (and a chip on the diary index) is a contained render_diary.mjs follow-up, filed as its own small hub issue, not blocking v0.2.

7. Session-skill changes (obot.agent)

8. Decisions Resolved 2026-07-11

Carried from the issue's Design placeholder; each was presented to @jwildfire with a recommendation as the gate on this design. All four resolved 2026-07-11 — recommendations accepted as written.

D1 — Refresh mechanism: static watch loop, or a tiny local server? Approved 2026-07-11
Resolved: static watch loop. --watch regenerates every ~60 s; the page auto-refreshes; Chrome reads it from file://. Zero dependencies, zero ports, nothing extra to babysit — and it keeps the “read-only static generator” promise in the requirement. A local server's only real win is flicker-free partial refresh, which invites interactivity scope creep; revisit only if the full-page refresh annoys in practice (a scroll-position-restore snippet is the cheap mitigation).
D2 — Report placement and diary integration? Approved 2026-07-11
Resolved: reports/sessions/{diary-slug}.html + link-first diary integration. Slug mirrors the diary entry so report and entry are permanently paired; the wrapup adds a one-line link in the entry (no render_diary.mjs change needed now). The proper diary tab + index chip is a contained follow-up issue on render_diary.mjs, post-v0.2.
D3 — ## Scaffold capture UX: dedicated skill, or a session-note extension? Approved 2026-07-11
Resolved: dedicated session-scaffold skill. The existing pattern is one thin skill per capture channel (session-note → Notes, session-update → Todo); a separate skill keeps triggers unambiguous for both Jeremy and agents, and the wrapup sweep targets exactly one section. Overloading session-note with a flag saves one file but muddies the trigger phrase.
D4 — Session boundary: calendar day, or job cluster? Approved 2026-07-11
Resolved: lifecycle boundary — session-init marker to wrapup — with calendar-day fallback. Anchor on the newest <!-- session-init … --> marker in today's scratchpad; include jobs updated since it (plus still-running carryovers); report slug takes the diary session number. This matches the diary's per-working-session convention exactly, and handles both real cases from this week: two sessions in one day (07-11 → -2) and one session spanning midnight (07-10 21:30 → 07-11 08:10). A pure calendar-day boundary mishandles both.

9. Out of scope

10. Risks

RiskMitigation
state.json is a harness-internal format, not a documented API; fields may rename on CLI upgrades. Pin the §2 field list in one parser module; treat everything else as opaque; per-agent degradation (row renders with whatever fields survive + a notice); re-verify on CLI upgrades. Carried over verbatim from the issue's known-risk note.
Scratchpad format drift — it's prose maintained by several agents. Tolerant section parser: recognized structures (checkboxes, timestamps, refs) get rich rendering; anything else passes through as plain text rather than being dropped.
gh sweep: rate limits, offline, SAML-blocked orgs. Single batched sweep, cached with TTL; on failure the panel shows the last cache + its timestamp; Gilead-BioStats excluded by construction.
Report snapshots drift from the diary narrative (two records of one session). They are different views by design — report = operational record (frozen data), diary = narrative. The shared slug + mutual link keeps them paired; neither restates the other.

11. Acceptance criteria

  1. Live: one command from the workspace renders every §5 panel from a real session's data; opened in Chrome from file://, check-states, agent states, and token counts match the underlying sources at render time; the watch loop keeps it current unattended.
  2. Degradation: removing a pinned state.json field, an entire scratchpad section, or network access still yields a rendered page with per-panel notices — no crash, no blank page.
  3. Report: a real session's wrapup produces reports/sessions/{slug}.html, committed to the hub, linked from that day's diary entry, live on the Pages site — the first exemplar being the session that implements this.
  4. Scaffold channel: “scaffold: …” lands in ## Scaffold, shows on the dashboard, and the wrapup sweep processes the list.
  5. Footprint: no new state stores beyond the ## Scaffold section; roadmap activity remains derived + cached, never committed.

12. Task preview — the obot.agent v0.2 milestone

Decomposed via requirement-tasks into linked sub-issues after design sign-off — this preview is for shape and sizing only. #24 is the driver requirement for the obot.agent v0.2 milestone.

#RepoTaskSize
1obot.agenttools/session-hub/ generator: collectors + session model + live-mode renderer (--watch), degradation behavior, READMEM
2obot.agentReport mode (--report, freeze semantics) + session-wrapup report stepS–M
3obot.agentsession-scaffold skill + scratchpad skeleton (## Scaffold) + session-init/wrapup text updates; workspace wiring (symlink, allowlist)S
4obot.roadmapreports/sessions/ home (README, index listing) + diary link convention; supersession note on the 2026-07-04 orchestration reportS
obot.roadmapFollow-up (post-v0.2): diary tab/index chip in render_diary.mjsS