|
| 1 | +# Session Dash Panel Design |
| 2 | + |
| 3 | +Auto-generated single-page control panel that tracks agent session task |
| 4 | +progress and result statistics from LoopX public-safe projections. |
| 5 | + |
| 6 | +## Problem |
| 7 | + |
| 8 | +Operators want a compact, copyable web view of **what the fleet is doing |
| 9 | +right now**: which sessions (agent runtimes) exist, how many goals each owns, |
| 10 | +and what state each goal is in. The existing React dashboard covers this |
| 11 | +interactively, but it requires a Node build, a dev server, and browser |
| 12 | +tooling. A loopback single-page panel is a lighter surface for quick local |
| 13 | +inspection: run one command from the project directory and keep the tab open |
| 14 | +while the agents work. |
| 15 | + |
| 16 | +The panel is intentionally **human-focused**. LoopX's internal control |
| 17 | +machinery (decision frames, work-lane contracts, quota slot math, truth |
| 18 | +contracts, source-warning diagnostics, lease/write-scope bookkeeping) is not |
| 19 | +rendered: it is noise to an operator and belongs to the control plane, not the |
| 20 | +watch surface. The page shows only the signal that answers "how is the work |
| 21 | +going?": |
| 22 | + |
| 23 | +1. **Overview** — sessions, goals, active / needs-you / blocked / done |
| 24 | + buckets, open todos, and run counts (the result statistics). |
| 25 | +2. **Sessions** — one card per session, with its state, role, goal count, and |
| 26 | + the goals it owns. |
| 27 | +3. **Per goal** — status badge, todo progress bar (done vs open agent/user), |
| 28 | + what it is waiting on, latest run time/classification, and next action. |
| 29 | + |
| 30 | +## Decision |
| 31 | + |
| 32 | +Add a **live single-page panel** (`loopx dash`) that serves the fleet snapshot |
| 33 | +from existing public-safe projections: |
| 34 | + |
| 35 | +1. Collect the same inputs the dashboard already consumes: the status contract |
| 36 | + (`attention_queue.items[]`), `run_history.goals[]`, the todo index, the |
| 37 | + agent-management projection (sessions + their `goal_ids`), and the usage |
| 38 | + summary. |
| 39 | +2. Fold them through a read-only fleet projection |
| 40 | + (`build_session_dash_projection`, schema `session_dash_projection_v1`) that |
| 41 | + groups goals under sessions and computes the overview buckets. |
| 42 | +3. Render a single HTML page with a small no-dependency renderer, reusing the |
| 43 | + patterns in `loopx/presentation/renderers/goal_channel_html.py`. |
| 44 | +4. Serve it from a loopback HTTP server (`loopx/dash_server.py`) that also |
| 45 | + exposes a `/panel` fragment and `/status.json` projection; the page |
| 46 | + auto-refreshes in place by polling `/panel`. |
| 47 | +5. Keep `loopx dash generate` for a one-shot static HTML snapshot, with the |
| 48 | + existing public/private boundary scan enforced before success. |
| 49 | + |
| 50 | +The React dashboard stays the interactive surface; the loopback panel is the |
| 51 | +fast watch surface. They share the same projections and data contract, so the |
| 52 | +panel cannot drift into a second source of truth. |
| 53 | + |
| 54 | +## Repository Placement |
| 55 | + |
| 56 | +| Concern | Location | Reason | |
| 57 | +| --- | --- | --- | |
| 58 | +| Renderer | `loopx/presentation/renderers/session_dash_html.py` | Pure renderer over already-built payloads, per the presentation surface layout. | |
| 59 | +| Projection assembly | `loopx/presentation/projections/session_dash.py` | Intermediate public-safe read model (fleet snapshot). Reuse existing builders when possible; do not duplicate contract parsing. | |
| 60 | +| Generation command | `loopx/cli_commands/dash.py` + `loopx/dash_server.py` | Operator-facing CLI entry (`loopx dash` serves, `dash generate` exports); reads status/quota/todos exactly like `serve-status` does. | |
| 61 | +| Static export | Reuse the boundary scan + `loopx dash generate` | Existing public/private scanner; manifest/revision receipt stays with the static-site pipeline when needed. | |
| 62 | +| Docs | `docs/product/surfaces/` + dashboard README | Same documentation home as other presentation surfaces. | |
| 63 | +| Validation | `examples/session-dash-panel-smoke.py` | Public-safe fixture smoke, no live state required. | |
| 64 | + |
| 65 | +This is a presentation surface, not a new capability: it does not own state, |
| 66 | +quota, gates, or authority. Per the capability placement guide, it stays in the |
| 67 | +presentation layer and reuses existing control-plane contracts. |
| 68 | + |
| 69 | +## Data Contract (Inputs) |
| 70 | + |
| 71 | +The projection consumes only public-safe projections: |
| 72 | + |
| 73 | +- `loopx status` JSON, schema_version 2: `attention_queue.items[]` (per-goal |
| 74 | + waiting_on / recommended_action), `run_history.goals[]` (goal status, |
| 75 | + lifecycle phase, latest runs), `todo_index.items[]` (per-goal role/status), |
| 76 | + `agent_management_projection.agents[]` (sessions: state, role, `goal_ids`, |
| 77 | + next action, last activity), and `usage_summary.totals` (runs 24h/7d). |
| 78 | +- Public-safe evidence pointers only; never raw transcripts, logs, credentials, |
| 79 | + or local paths. |
| 80 | + |
| 81 | +Raw-looking keys found in inputs are recorded as boundary warnings without |
| 82 | +copying values, matching `goal_channel_projection` behavior. |
| 83 | + |
| 84 | +## Projection Shape |
| 85 | + |
| 86 | +`schema_version: session_dash_projection_v1`, `mode: read_only`: |
| 87 | + |
| 88 | +- `overview` — session/goal/run counts, `goals_by_status` buckets |
| 89 | + (active / needs_user / blocked / done / other), open agent/user todos, done |
| 90 | + todos, runs 24h/7d. |
| 91 | +- `sessions[]` — one entry per agent runtime: `session_id`, `role`, `state`, |
| 92 | + `next_action`, `last_activity_at`, `goal_count`, and `goals[]`. |
| 93 | +- `goals[]` — `goal_id`, `display_name`, `domain`, `status`, `status_bucket`, |
| 94 | + `waiting_on`, open/done todo counts, latest run time + classification, and |
| 95 | + `next_action`. |
| 96 | +- `unassigned_goals[]` — goals no session declares, so nothing silently |
| 97 | + disappears from the operator's view. |
| 98 | +- `focus_goal_id` — when `--goal-id` is passed, the snapshot narrows to |
| 99 | + sessions containing that goal. |
| 100 | + |
| 101 | +## Page Layout (Single Page) |
| 102 | + |
| 103 | +The single page renders these sections in order: |
| 104 | + |
| 105 | +1. **Header**: panel title, generated-at time, read-only marker; a focus pill |
| 106 | + when `--goal-id` is set. |
| 107 | +2. **Overview strip**: sessions, goals, active, needs-you, blocked, done, open |
| 108 | + todos, runs (24h). |
| 109 | +3. **Session cards**: per session, its state badge, role, goal count, last |
| 110 | + activity, and next action; below it, a goal table with status badges, |
| 111 | + todo progress bars, waiting reason, latest run, and next action. |
| 112 | +4. **Goals without a session** (when present): same goal table for goals no |
| 113 | + session claims. |
| 114 | + |
| 115 | +All data is rendered from the projections above; the page contains no write |
| 116 | +controls and no browser write authority. |
| 117 | + |
| 118 | +## Command Surface |
| 119 | + |
| 120 | +The primary entry point is a live server: run `loopx dash` inside a project |
| 121 | +checkout and open the printed loopback URL in a browser. The single page |
| 122 | +tracks the fleet's session task progress/status and refreshes itself in place |
| 123 | +(default every 10s) by re-fetching the `/panel` fragment, so the operator can |
| 124 | +keep the tab open while the agents work. |
| 125 | + |
| 126 | +```bash |
| 127 | +loopx dash # serve the fleet panel at http://127.0.0.1:8767/ |
| 128 | +loopx dash --goal-id <id> # narrow the panel to one goal |
| 129 | +loopx dash --port 9000 --refresh-seconds 5 |
| 130 | +``` |
| 131 | + |
| 132 | +Routes: |
| 133 | + |
| 134 | +- `GET /` — full single-page panel with the in-place auto-refresh script. |
| 135 | +- `GET /panel` — fresh `<main>` fragment for the refresh script. |
| 136 | +- `GET /status.json` — the compact session dash projection as JSON. |
| 137 | +- `GET /healthz` — health probe. |
| 138 | + |
| 139 | +A one-shot static snapshot remains available: |
| 140 | + |
| 141 | +```bash |
| 142 | +loopx dash generate [--goal-id <id>] [--out dash.html] |
| 143 | +``` |
| 144 | + |
| 145 | +- Without `--out`, print the HTML to stdout (or use `--format json` for the |
| 146 | + projection + html payload). |
| 147 | +- With `--out`, write the file and run the public boundary scan before |
| 148 | + reporting success. |
| 149 | +- The command is read-only: it never mutates todos, quota, gates, or registry. |
| 150 | +- The server binds loopback only (`127.0.0.1`); it exposes no write routes. |
| 151 | + |
| 152 | +## Public/Private Boundary |
| 153 | + |
| 154 | +- Inputs are restricted to the status contract and public-safe projections. |
| 155 | +- The renderer escapes all text and never inlines raw payload values. |
| 156 | +- Static export reuses the existing boundary scanner (absolute local paths, |
| 157 | + private keys, credentials, tokens) before success. |
| 158 | +- A negative fixture proves the generated page rejects private material; the |
| 159 | + smoke asserts the boundary, not the exact prose. |
| 160 | + |
| 161 | +## Validation |
| 162 | + |
| 163 | +`examples/session-dash-panel-smoke.py` (Python, no browser required): |
| 164 | + |
| 165 | +1. Builds a public-safe fleet fixture (two goals, one session owning a second |
| 166 | + goal with finished todos). |
| 167 | +2. Renders the page and asserts read-only markers, the overview + session |
| 168 | + panels, session -> goal grouping, progress bars, escaped output, the live |
| 169 | + refresh script, and the `/panel` fragment shape. |
| 170 | +3. Asserts internal machinery (decision frame, work lane, truth contract, |
| 171 | + source warnings, leases) is absent from the page. |
| 172 | +4. Injects synthetic private markers (`GH_FAKE_*` style) and asserts they stay |
| 173 | + out of the rendered page / static export. |
| 174 | + |
| 175 | +## Out Of Scope (For Now) |
| 176 | + |
| 177 | +- Browser write controls: the dashboard remains the only surface that may |
| 178 | + submit reward/control-plane drafts, and only through explicit loopback |
| 179 | + capability gates. |
| 180 | +- New capability or provider: none. This is a renderer + CLI presentation |
| 181 | + surface over existing contracts. |
| 182 | +- Goal-level drill-down inside the fleet panel: the per-goal channel details |
| 183 | + remain on the React dashboard; the loopback panel is the at-a-glance watch |
| 184 | + surface. |
0 commit comments