224 lines
14 KiB
Markdown
224 lines
14 KiB
Markdown
---
|
|
status: draft
|
|
created: 2026-05-02
|
|
owner: cfl
|
|
---
|
|
|
|
# Office Dashboard
|
|
|
|
## Why
|
|
|
|
The office dashboard is the first place a user looks to understand workspace health at a glance: which agents are working, how runs and tasks are trending, what changed recently, and how much the workspace is spending. Before this work it showed 4 metric cards and a recent-activity feed with raw UUIDs, with no visibility into per-agent state, run trends, or recently updated tasks.
|
|
|
|
## What
|
|
|
|
The dashboard is laid out top-to-bottom as:
|
|
|
|
1. Per-agent cards panel
|
|
2. Clickable stat cards (Agents Enabled, Tasks In Progress, Month Spend, Pending Approvals)
|
|
3. Chart grid (Run Activity, Tasks by Priority, Tasks by Status, Success Rate)
|
|
4. Recent Tasks section
|
|
5. Recent Activity feed
|
|
|
|
Live-update behaviour (WS events, sidebar live indicators, dashboard reactivity) is specified in `live-updates.md` and is not repeated here.
|
|
|
|
### Per-agent cards panel
|
|
|
|
- Renders **one card per workspace agent**, listed in the order agents are configured. The panel always renders, even when no agent has ever run; it never collapses to empty.
|
|
- Cards are laid out in a responsive grid: 1 / 2 / 4 columns based on viewport. All cards share the same width regardless of state, so the layout does not jump as state changes.
|
|
- Sort order: agents with a live session first, then by most-recent session, then alphabetically. Sort is stable so minor state changes do not reorder the panel.
|
|
|
|
Each card has three observable states:
|
|
|
|
| State | Trigger | Visual |
|
|
|---|---|---|
|
|
| `live` | Agent has any session in `RUNNING` | Pulsing emerald dot, `Live now` subtitle, current task pill, expanded recent runs |
|
|
| `finished` | Agent has at least one historical session, none currently `RUNNING` | Muted dot, `Finished {relativeTime}` subtitle, last task pill, expanded recent runs |
|
|
| `never_run` | Agent has zero session history | Muted card with agent identity only |
|
|
|
|
Card anatomy:
|
|
|
|
1. **Header** - pulsing dot when live, agent avatar + display name, external-link icon linking to `/office/agents/{id}`.
|
|
2. **Subtitle** - `Live now`, `Finished {relativeTime}`, or `Never run`.
|
|
3. **Current task pill** (when there is a most-recent session) - `{identifier} - {title}` (e.g. `KAN-3 - present yourself`); links to `/office/tasks/{taskId}`.
|
|
4. **Expanded run section** - up to 5 most recent sessions, one row each. Scrollable when long.
|
|
- Completed row: `{Agent} worked for {duration} - ran {N} commands`
|
|
- Active row: `working for {duration}` (animated)
|
|
- Each row carries a relative-time stamp of session start.
|
|
|
|
### Clickable stat cards
|
|
|
|
Four metric cards at the top of the chart area. Each card is a link to its detail page and shows a richer breakdown subtitle (not the prior generic description).
|
|
|
|
| Card | Subtitle example | Links to |
|
|
|---|---|---|
|
|
| Agents Enabled | `2 running, 1 paused, 0 errors` | `/office/agents` |
|
|
| Tasks In Progress | `13 open, 1 blocked` | `/office/tasks` |
|
|
| Month Spend | (current period total + delta) | `/office/workspace/costs` |
|
|
| Pending Approvals | (count of pending approval requests) | `/office/inbox` |
|
|
|
|
### Charts
|
|
|
|
Four visualisations rendered with pure CSS bars (no charting library). All share a 14-day x-axis with date labels at days 0, 6, and 13. The tallest bar on a chart scales to 100% height; other bars scale proportionally.
|
|
|
|
| Chart | Encoding |
|
|
|---|---|
|
|
| Run Activity | Stacked bars per day: succeeded (green), failed (red), other (gray) |
|
|
| Tasks by Priority | Stacked bars per day: tasks created, grouped by priority (critical, high, medium, low) |
|
|
| Tasks by Status | Stacked bars per day: tasks created, grouped by status (todo, in_progress, done, blocked) |
|
|
| Success Rate | Single bar per day: percentage of succeeded runs. Colour: green (>=80%), yellow (50-79%), red (<50%) |
|
|
|
|
### Recent Tasks
|
|
|
|
A list of the 10 most recently updated tasks. Each row: status icon, identifier (monospace), title (truncated), assignee agent name, relative timestamp. Each row links to the task detail page.
|
|
|
|
### Recent Activity feed
|
|
|
|
A reverse-chronological feed of workspace activity. Raw UUIDs are resolved to human-readable names:
|
|
|
|
- Task IDs are rendered as identifiers (e.g. `KAN-14`).
|
|
- Agent IDs are rendered as agent names.
|
|
- Wakeup IDs are hidden entirely.
|
|
- Action descriptions are formatted as readable sentences: `CEO completed task KAN-14`, not `wakeup cancelled stale wakeup 64a8cdcd-...`.
|
|
- The `system` actor is rendered with a `SY` badge; agent actors are rendered with the agent's initials.
|
|
|
|
## Data model
|
|
|
|
The dashboard reads from existing office tables; no new tables are introduced for the dashboard surface itself. The agent-cards panel adds a new derived/aggregated read path.
|
|
|
|
The agent-cards panel relies on:
|
|
- `office_agents` (workspace agent instances)
|
|
- `task_sessions` joined by `agent_instance_id`, ordered `started_at DESC`
|
|
- `office_tasks` for `task_identifier` and `task_title`
|
|
- `messages` for command counts (`type = 'tool_call'` count per session)
|
|
|
|
For the `status` field, an office session in state `IDLE` counts as `finished` (not `live`).
|
|
|
|
## API surface
|
|
|
|
The dashboard frontend hits an existing dashboard endpoint plus a new per-agent summaries endpoint introduced for the per-agent cards panel.
|
|
|
|
### Agent summaries
|
|
|
|
```
|
|
GET /api/v1/office/workspaces/:wsId/agent-summaries
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"agents": [
|
|
{
|
|
"agent_id": "...",
|
|
"agent_name": "CEO",
|
|
"agent_role": "ceo",
|
|
"status": "live | finished | never_run",
|
|
"live_session": {
|
|
"session_id": "...",
|
|
"task_id": "...",
|
|
"task_identifier": "KAN-3",
|
|
"task_title": "present yourself",
|
|
"started_at": "2026-05-04T..."
|
|
},
|
|
"last_session": {
|
|
"session_id": "...",
|
|
"task_id": "...",
|
|
"task_identifier": "KAN-3",
|
|
"task_title": "present yourself",
|
|
"started_at": "...",
|
|
"completed_at": "..."
|
|
},
|
|
"recent_sessions": [
|
|
{
|
|
"session_id": "...",
|
|
"task_id": "...",
|
|
"task_identifier": "KAN-3",
|
|
"task_title": "present yourself",
|
|
"state": "RUNNING | IDLE | COMPLETED | FAILED | CANCELLED",
|
|
"started_at": "...",
|
|
"completed_at": "...",
|
|
"command_count": 3
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Semantics:
|
|
- `live_session` is `null` unless `status == "live"`.
|
|
- `last_session` is `null` unless the agent has at least one session.
|
|
- `completed_at` may be `null` for office sessions still in `IDLE`.
|
|
- `recent_sessions` returns up to 5 entries, most recent first.
|
|
|
|
The pre-existing `GET /workspaces/:wsId/live-runs` endpoint stays for backward compatibility and continues to return the prior per-run shape it returned before agent cards landed.
|
|
|
|
### Dashboard payload
|
|
|
|
The existing `GET /api/v1/office/workspaces/:wsId/dashboard` endpoint backs the stat cards, charts, Recent Tasks list, and Recent Activity feed. (Shape evolves alongside the dashboard; cross-link to the implementation when adding new sections rather than re-restating here.)
|
|
|
|
## Permissions
|
|
|
|
The dashboard is a read-mostly surface scoped to a single workspace. Authorization rules:
|
|
|
|
- **Workspace scoping.** Every dashboard endpoint takes `:wsId` in the path. The `agents.AgentAuthMiddleware` rejects any agent-JWT request whose `claims.WorkspaceID` does not match `:wsId` with `403 token workspace mismatch`. UI callers (no JWT) currently pass through unchecked — single-user kandev does not yet have a user-session auth layer, so the dashboard treats all UI callers as the singleton human user (`decider_id = "user"`, `inbox_dismissals.user_id = "default"`).
|
|
- **Read endpoints** (`GET /dashboard`, `/agent-summaries`, `/agents/:id/summary`, `/agents/:id/runs`, `/agents/:id/runs/:runId`, `/inbox`, `/activity`, `/runs`, `/tasks`, `/tasks/search`, `/live-runs`) require only that the caller resolve to the workspace. No per-agent visibility filtering is applied — every agent in the workspace can see every other agent's cards, runs, and activity. The per-agent inbox view (`GetAgentInboxItems`) is invoked internally for agent-scoped wakeups and returns only that agent's pending review/approval requests; it is not a separate HTTP route.
|
|
- **Mutating endpoints** carry the same permission keys defined in `internal/office/shared/permissions.go`:
|
|
- `POST /tasks/:id/approve`, `POST /tasks/:id/request-changes`: agent callers require `can_approve`. Cross-workspace decisions are rejected with `403`. An agent cannot decide an approval it requested (self-approval guard).
|
|
- `POST /tasks/:id/blockers`, `DELETE /tasks/:id/blockers/:blockerId`, reviewer/approver mutations: open to all callers (no permission gate today).
|
|
- `PATCH /tasks/:id` assignee mutation surfaces `shared.ErrForbidden` as `403` for agent callers without the required permission, anything else as `500`.
|
|
- `POST /inbox/dismiss`: requires the `MarkFixedHandler` to be wired (`503` otherwise) and resolves under `user_id = "default"`.
|
|
- Settings mutation (`PATCH /workspaces/:wsId/settings`): no gate today; UI-only.
|
|
- **Activity feed entries** carry `actor_type ∈ {user, agent, system}` and `actor_id` (user id, agent instance id, or literal `"system"`). The feed does not redact entries per viewer — every workspace member sees the full log.
|
|
- **Routing endpoints** (`/workspaces/:wsId/routing*`, `/agents/:id/route`, `/runs/:id/attempts`) are scoped the same way; their authorization rules live in `office/routing.md`.
|
|
|
|
See `agents/` spec for the role → permission defaults and the no-escalation rule that governs the underlying permission keys.
|
|
|
|
## Persistence guarantees
|
|
|
|
The dashboard is a thin projection over durable office tables. Nothing the dashboard surfaces is held in process memory across the request boundary; on restart the next dashboard fetch recomputes everything from SQLite.
|
|
|
|
**Durable (survives restart):**
|
|
|
|
- Recent Activity feed and the status-change timeline reads `office_activity_log` rows; entries are append-only and have no TTL.
|
|
- Pending Approvals card count reads `office_approvals` rows; `pending` rows persist until the user decides them.
|
|
- Stat cards, charts, Recent Tasks, Tasks by Status / Priority — derived from `tasks`, `office_agents`, `office_runs`, `messages`, `office_cost_events`. None of this data is dashboard-owned.
|
|
- Run Activity / Success Rate bars query `office_runs` for the 14-day window; rows older than the window stay in the DB but are not visualised.
|
|
- Inbox dismissals persist in `office_inbox_dismissals` keyed by `(user_id, item_kind, item_id)` so a "Mark fixed" decision survives restart.
|
|
- Workspace governance flags (`require_approval_for_new_agents`, `require_approval_for_task_completion`, `require_approval_for_skill_changes`) persist in `office_workspace_governance`; `permission_handling_mode` and `recovery_lookback_hours` persist on the workspace's filesystem config via the `SettingsProvider` adapter.
|
|
- Workflow-domain decisions (used to compute "viewer needs decision" on inbox review-request items) persist in `workflow_step_decisions` and are superseded — never deleted — when re-recorded.
|
|
|
|
**Transient (recomputed on every read):**
|
|
|
|
- Per-agent cards `status` (`live` / `finished` / `never_run`), the sort order, `recent_sessions`, and `command_count` are computed live by `GetAgentSummaries` from `office_agents` + `task_sessions` + `messages` at request time. There is no cached cards payload; a kandev restart serves identical content from the first fetch onward.
|
|
- Inbox is a computed view (see `inbox.md` Persistence guarantees) — no `office_inbox` table exists. The badge count and the listing both recompute on every fetch.
|
|
- Frontend store hydration: SSR fetches dashboard data once on page load and hydrates the Zustand store. Live updates are WS-driven (`live-updates.md`); on hard reload the SSR fetch reseeds the store. WS subscriptions are not durable — the gateway re-subscribes on reconnect.
|
|
- The dashboard does NOT cache anything backend-side beyond the request lifetime; there is no Redis/in-memory dashboard cache. Adding one would require explicit invalidation hooks and is intentionally not implemented.
|
|
|
|
## Scenarios
|
|
|
|
- **GIVEN** a workspace with N agents configured and no recent sessions, **WHEN** the user opens the dashboard, **THEN** the agent cards panel renders exactly N cards, each in `never_run` state with no live indicator and no task pill.
|
|
|
|
- **GIVEN** a workspace with 2 agents, 1 running and 1 idle, **WHEN** the user opens the dashboard, **THEN** the running agent's card shows a pulsing dot, `Live now` subtitle, and current task pill; the idle agent's card shows a muted dot and `Finished {relativeTime}` subtitle.
|
|
|
|
- **GIVEN** 14 days of agent run history with some failures, **WHEN** the user views the Run Activity chart, **THEN** stacked bars show green / red / gray segments per day with the tallest bar scaled to 100% height.
|
|
|
|
- **GIVEN** a workspace with 13 open tasks and 1 blocked task, **WHEN** the user views the Tasks In Progress card, **THEN** the card shows the in-progress count with subtitle `13 open, 1 blocked`, and clicking it navigates to `/office/tasks`.
|
|
|
|
- **GIVEN** 10 recently updated tasks, **WHEN** the user views the Recent Tasks section, **THEN** each row shows the task identifier (monospace), title, assignee agent, and relative timestamp, and clicking a row opens the task detail page.
|
|
|
|
- **GIVEN** the Recent Activity feed contains a wakeup-cancellation entry, **WHEN** the user views the feed, **THEN** the row reads as a readable sentence with no raw UUIDs, the actor badge is `SY` for system actors, and any task references render as identifiers (e.g. `KAN-14`).
|
|
|
|
- **GIVEN** a workspace with mixed agent states, **WHEN** the agent cards panel sorts, **THEN** live agents render first, then by most-recent session, then alphabetically; the sort is stable across re-renders.
|
|
|
|
## Out of scope
|
|
|
|
- Live streaming of agent transcripts inside dashboard cards. v1 of the per-agent expanded run rows is header-only; an embedded `<AdvancedChatPanel>` per row may follow once dashboard visual weight is reassessed.
|
|
- Per-card cost / utilisation stats - existing dashboard-level metrics already cover this.
|
|
- Agent grouping (e.g. by role). The panel is a flat list, sorted by activity.
|
|
- Plugin widget slots.
|
|
- Budget incident banners (separate feature).
|
|
- Cost breakdown charts - belongs in the costs page.
|
|
- Polling. All dashboard live behaviour is specified in `live-updates.md` and is WS-driven.
|