Skip to content

Commit cc92357

Browse files
committed
feat(dash): live session dash panel focused on human-readable fleet progress
Adds `loopx dash`: a loopback single-page control panel that tracks agent session task progress and result statistics in real time, organized the way an operator reads it — sessions (agent runtimes) on top, the goals each session owns underneath, and per goal the status badge, todo progress bar, waiting reason, latest run, and next action. - `loopx dash` serves at http://127.0.0.1:8767/ with in-place auto-refresh (default 10s) by polling the `/panel` fragment; `--goal-id` narrows the fleet view to one goal. - `loopx dash generate` keeps the one-shot static HTML snapshot, with the public/private boundary scan enforced before success. - Projection `session_dash_projection_v1` folds the status contract (attention queue, run-history goals, todo index, agent-management sessions, usage summary) into a compact fleet snapshot; internal control machinery (decision frames, work-lane contracts, quota slot math, truth contracts, source warnings, lease bookkeeping) is intentionally not rendered. - Routes: GET /, GET /panel, GET /status.json, GET /healthz. Loopback-only; no write routes, read-only markers, escaped output + boundary scan. - Docs: design doc, dashboard README run guide, README surface row. - Validation: examples/session-dash-panel-smoke.py (fleet fixture, read-only markers, refresh script, fragment shape, boundary negative controls, XSS escape, focus filter). Squashed from the PR #2897 branch history.
1 parent 7a7ae8a commit cc92357

14 files changed

Lines changed: 1772 additions & 14 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ LoopX folds its control-plane mechanics into five questions:
249249
| Quota and interaction contract | Decides whether a turn should deliver, ask, wait, self-repair, or stay quiet. | `loopx quota should-run`, [quota allocation](docs/quota-allocation.md) |
250250
| Agent runtime bridges | Keeps Codex App, Codex CLI, Claude Code, and generic workers aligned with the same guard. | `loopx heartbeat-prompt`, `loopx codex-cli-bootstrap-message`, `loopx worker-bridge` |
251251
| Operator surfaces | Renders compact status without making the browser the state authority. | `loopx serve-status`, [dashboard](apps/presentation/dashboard/README.md) |
252+
| Session dash | Starts a live single-page panel that tracks fleet progress: sessions, their goals, and each goal's status/todo progress, with result statistics; auto-refreshes in place. | `loopx dash`, [session dash design](docs/product/surfaces/session-dash-panel-design.md) |
252253
| External projections | Projects todos and gates into collaboration surfaces while LoopX remains authoritative. | `loopx lark-kanban`, [Lark Kanban adapter](docs/integrations/lark-kanban-control-plane-adapter.md) |
253254
| Domain capabilities | Packages repeatable work lanes such as issue fixing, content operations, value connector planning, ML experiment advice, benchmark evidence, and Explore. | `loopx issue-fix`, `loopx content-ops`, `loopx value-connectors`, `loopx ml-experiment`, `loopx benchmark`, [Explore](docs/capabilities/explore/README.md) |
254255
| Experimental context learning | Lets named registered agents trial provider-neutral Reward Memory through ignored, default-off project configuration. OpenViking is one provider option, not a global dependency. | `loopx reward-memory experiment-status`, [Reward Memory architecture](docs/reference/protocols/reward-memory-architecture-v0.md) |

apps/presentation/dashboard/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,43 @@ local inspection file only. For public demos, use the sanitized
281281
You can also import a JSON file directly in the browser, or load a local API
282282
URL that returns the same `loopx --format json status` shape.
283283

284+
## Live Single-Page Session Dash
285+
286+
The primary way to watch session task progress is a loopback single-page panel:
287+
288+
```bash
289+
loopx dash # serve at http://127.0.0.1:8767/ (auto-refresh every 10s)
290+
loopx dash --goal-id <goal-id> # narrow the panel to one goal
291+
```
292+
293+
Open the printed URL in any browser and keep it open while the agents work.
294+
The page is a human-focused fleet view: an overview strip of sessions, goals,
295+
active / needs-you / blocked / done buckets, open todos and run statistics,
296+
followed by one card per session with the goals it owns and each goal's
297+
status badge, todo progress bar, waiting reason, and latest run. It refreshes
298+
itself in place every 10 seconds by re-fetching the `/panel` fragment.
299+
Internal control machinery (decision frames, work-lane contracts, quota slot
300+
math, source warnings) is intentionally not rendered. The panel is
301+
read-only: no write controls, no browser write authority. The server binds
302+
loopback only and exposes no write routes.
303+
304+
A one-shot static snapshot is also available for demos or sharing:
305+
306+
```bash
307+
loopx dash generate [--goal-id <goal-id>] --out dash.html
308+
```
309+
310+
Open `dash.html` in any browser. The command runs the public/private
311+
boundary scan before reporting success and withholds output on failure.
312+
313+
```bash
314+
# print the projection + html as JSON instead
315+
loopx --format json dash generate --goal-id <goal-id>
316+
```
317+
318+
See [the session dash panel design](../../../docs/product/surfaces/session-dash-panel-design.md)
319+
for the layout, data boundary, and validation contract.
320+
284321
## Browser Smokes
285322

286323
Dashboard browser smokes are explicit because they start a temporary Vite

docs/product/surfaces/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
- [Frontstage dashboard interaction baseline](frontstage-dashboard-interaction-baseline.md)
88
- [Frontstage two-surface strategy](frontstage-two-surface-strategy.md)
99
- [Presentation surface layout](presentation-surface-layout.md)
10+
- [Session dash panel design](session-dash-panel-design.md)
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
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

Comments
 (0)