UI Dashboard - Information Architecture and Desktop Shell
Information Architecture
Global Layout
Top Navigation Bar
- Logo/brand (
pam) - Activity feed button (right side, notification badge for recent events)
- Orchestrator status indicator (green/red dot)
- Hosts summary chip (online/total worker hosts — click navigates to
/hosts) - Settings button
Main Content Area
- Single-page application with client-side routing
- Persistent activity feed drawer (collapsible, right side)
Page Hierarchy
1. Projects List Page (/projects)
Purpose: Overview and navigation to all projects.
Layout: Grid of project cards.
Per Project Card:
- Project name (click to navigate to project detail)
- Slug badge
- Status indicator (active/archived)
- Task count (total, broken down by status: todo/doing/done)
- Last activity timestamp
- Quick action button (menu: archive, settings)
Empty State: “No projects yet. Create your first project to get started.”
Sorting: Default by last activity, option to sort by name or creation date.
2. Project Detail Page (/projects/{id})
Purpose: Project work items hierarchy and Kanban board.
Layout: Split view.
Left Panel - Work Items Tree:
- Hierarchical tree of milestones → epics → tasks → subtasks
- Expand/collapse nodes
- Status icons per item
- Click to navigate to task detail
- Filter controls (by kind, by status, by assignee)
Right Panel - Kanban Board:
- Four columns: Todo, Doing, Done, Cancelled
- Cards show: title, assignee badge, run count (if any), subtask count
- Drag-and-drop between columns (PATCH status via API); legal drops follow the work-item transition table in 002-data-model.md, including a direct Todo → Done drag — a task completed without entering Doing (finished out-of-band, or its run completed while
auto_mark_doneis off) skips the intermediate column - Click card to navigate to task detail
- “New Task” button (opens modal for quick creation)
Breadcrumbs: Projects → {project name}
3. Task Detail Page (/tasks/{id})
Purpose: Task information, run history, and live terminal pane.
Layout: Vertical sections.
Header:
- Task title (editable inline)
- Status badge (todo/doing/done/cancelled) - clickable to change
- Assignee field (editable)
- Kind badge (task/subtask)
- Path display (parent hierarchy links)
Description Section:
- Markdown editor/view toggle
- Empty state: “Add a description…”
Runs Section:
- List of runs (table: provider, session name, status, timestamps, actions)
- “Start Run” button (opens modal: provider selection)
- Per-run actions: logs, stop button (if running), attach button
Live Terminal Pane:
- Embedded xterm.js terminal (read-only)
- Connects to WS
/api/v1/runs/{id}/pane/ws - Shows latest pane snapshot
- Auto-updates on server push
- Empty state: “No active terminal session”
Metadata Footer:
- Created/updated timestamps
- Parent/child relationship links
Breadcrumbs: Projects → {project} → {task} or Projects → {project} → {milestone} → {epic} → {task}
4. Run View Page (/runs/{id})
Purpose: Detailed run information and interactive terminal access.
Layout: Split vertical.
Top Section - Run Info:
- Run ID (copy button)
- Task link (navigation)
- Provider badge (claude/codex/grok/gemini)
- Host badge (executing worker host name; offline hosts tinted with a warning)
- Session name (display:
pam-<8hex>) - Status badge (pending/spawning/running/exited/failed/stopped)
- Working directory path (on the executing host)
- Started/finished timestamps
- Exit code (if exited/failed)
- Actions: stop button (if running)
Middle Section - Interactive Terminal:
- xterm.js terminal (full interactive)
- “Attach” button (disabled if lease held by another client)
- “Detach” button (active when attached)
- Single-writer lease enforcement
- Connects to WS
/api/v1/runs/{id}/attach/ws
Bottom Section - Run Logs:
- Scrollable log history (pane snapshots over time)
- “Download Logs” button
Breadcrumbs: Projects → {project} → {task} → Run {id}
5. Hosts Page (/hosts)
Purpose: Worker fleet overview (multi-host topology, ADR-012).
Layout: Table of worker hosts.
Per Host Row:
- Host name + platform badge (macos/linux)
- Status (registered/online/offline/disabled)
- Last-seen timestamp, active run count
- Actions: disable/enable dispatch
Empty State: “No worker hosts registered. Run pam host add <name> --platform <platform> and start pam worker on that host.”
6. Activity Feed (Global Drawer)
Purpose: Real-time event journal across all resources.
Layout: Collapsible right drawer.
Feed Items (per event from SSE stream):
- Timestamp
- Event topic (project.created, work_item.updated, run.started, etc.)
- Resource links (clickable to navigate to relevant page)
- Contextual details (e.g., “Task ‘Fix bug’ moved to Done”)
Filters: Toggle by event type (project., work_item., run.*)
Empty State: “No recent activity”
7. Command Palette (Future Ambition)
Trigger: Cmd+K (or Ctrl+K)
Purpose: Quick navigation and action execution.
Features:
- Fuzzy search across projects, tasks, runs
- Quick actions: “Create task…”, “Start run…”, “Move to Done…”
- Keyboard navigation
- Deferred to post-MVP
SPA Stack
Core Technologies
- Leptos: Rust/WASM UI framework (CSR mode; fine-grained reactivity — no virtual DOM, no separate state library)
- wasm-bindgen / web-sys: browser interop layer
- xterm.js: the ONLY JavaScript dependency — browser terminal emulation has no mature Rust/WASM replacement; wrapped as a component driven through wasm-bindgen interop
State Management
- Leptos resources/signals: server-state synchronization
- Resources fetch projects, work items, runs; refetch on mutation
- Fine-grained reactivity drives board and live-view updates without manual cache invalidation (replaces TanStack Query)
- Optimistic updates for drag-and-drop board moves
- Leptos Router: client-side routing with typed params (replaces TanStack Router)
Terminal Rendering
- xterm.js (sole JS dependency, see Core Technologies): terminal emulator
- ANSI escape sequence rendering
- Read-only pane display (WS
/api/v1/runs/{id}/pane/ws) - Interactive attach session (WS
/api/v1/runs/{id}/attach/ws) - Canvas-based rendering (performance optimized)
Real-Time Features
- Server-Sent Events (SSE): activity feed event stream
web_sys::EventSourceconnection to/api/v1/events/stream- Topic filtering support
- Reconnect with Last-Event-ID cursor
- WebSocket: terminal pane and attach (
web_sys::WebSocket)- Auto-reconnect on disconnect
- Lease state tracking for attach
Development Mode
trunk dev server:
- Runs on port 5173 (default) with
/apiproxied to127.0.0.1:PAM_PORT - Hot rebuild of the WASM bundle
- CORS not needed (same-origin after proxy)
Command: trunk serve (from the SPA workspace member under web/)
Production Build
Build Process:
trunk build --releasecompiles the WASM bundle intoweb/dist/- Rust-embed includes
dist/in the binary - Axum serves embedded files at
/ - API routes at
/api/v1(unaffected)
Embedded File Structure:
web/dist/index.html→/web/dist/assets/*→/assets/*(WASM + the xterm.js bundle)- Fallback: all non-API routes serve
index.html(SPA routing)
Desktop Shell (Tauri v2 — Confirmed)
Shell Role: Monitoring Thin Client, Never an Execution Node
- Monitors the central orchestrator: hosts, runs, events, live terminals
- Views projects, work items, and other resources (board, detail, logs)
- User actions are forwarded as API requests ONLY — the orchestrator and its workers perform all work (spawning, supervision, provider execution stay in tmux on worker hosts)
- No local filesystem or shell capabilities required: configure Tauri capabilities with everything disabled (see 061-security.md)
- Target platforms: macOS and Linux desktops — matching the execution-node targets (ADR-011); Windows is out of scope and mobile is excluded. The shell itself is platform-independent by construction (it only loads the orchestrator URL), so no platform-specific shell code is anticipated
Comparison Record: Electron vs Tauri v2
Decision: Tauri v2 (Accepted 2026-08-29)
Decision: Ship web-first (embedded SPA in single binary) for MVP; the desktop shell is Tauri v2 — confirmed, no longer deferred. Web-first delivery still stands; only the shell implementation is scheduled for Phase 6.
Rationale:
- Web-first suffices for single-operator local use case: Embedded SPA in binary already provides “app-like” experience without overhead
- Size and memory: OS webview reuse (5-10 MB shell) vs ~150 MB bundled Chromium
- Rust alignment: single-language stack; Tauri’s Rust core matches the existing architecture with no Node.js sidecar
- Security surface: no Node.js runtime; a monitoring-only shell needs no local capabilities
Architecture Keeps Both Viable:
- SPA uses only standard web APIs (localStorage, fetch, WebSocket, EventSource)
- No Electron-specific APIs (ipcRenderer, remote) or Tauri-specific APIs (invoke) in SPA code
- Desktop shell is a thin wrapper that loads the same embedded UI
- Optionally runs
pam serveas a background process (sidecar mode)
Electron Fallback Preserved:
- If Tauri ecosystem gaps emerge (e.g., auto-update issues on macOS), Electron remains a drop-in fallback
- No lock-in: SPA is pure web, can be wrapped in either
Implementation Note (Phase 6):
- Tauri app loads the orchestrator URL (
PAM_API_URL— same artifact the browser uses) - Optionally spawns
pam serveas a child process (sidecar mode) — process management only; no execution logic in the shell - IPC bridge for tray icon, notifications, window management
- Rust core reused directly (no sidecar FFI needed)
Live Pane Rendering
Rendering Approach
Strategy: Server-sent full snapshots, client renders via xterm.js.
Server Behavior:
- Monitors tmux session output
- Detects pane changes (debounced 100ms)
- Sends full snapshot over WS pane WebSocket
- Includes sequence number for ordering
Client Behavior:
- Connects to WS
/api/v1/runs/{id}/pane/ws - Receives
{type: "pane_snapshot", seq, capture, timestamp}frames - Clears and rewrites terminal on each frame
- xterm.js handles ANSI escape sequences
Sequence namespace note (do not conflate): the pane seq on pane_snapshot frames and the run-signal seq carried by progress signals are two separate sequences in unrelated namespaces. The pane seq orders WebSocket snapshot frames for the terminal view; the signal seq orders repeated progress signals at the central ingest edge (030-http-api.md). Neither field is renamed — the distinction is deliberate.
Justification:
- Full snapshot simpler than delta encoding (no diff synchronization)
- Terminal pane size bounded (actual spawn size 220×50 ≈ 11,000 chars, ~11 KB per full snapshot)
- xterm.js efficiently handles full rewrites
- Delta complexity not worth it for small pane size
Throttling and Coalescing
Server-Side:
- Source capture polling is 1 Hz (snapshots emitted only on change)
- Coalesce rapid updates within 100ms window
- Max 10 frames per second for the interactive attach WS relay’s frame coalescing (prevents client overload)
- Priority to latest state (drops intermediate frames if lagging)
Client-Side:
- Request animation frame for rendering (60fps max)
- Skip frames if backpressure detected
Read-Only vs Attach UX Distinction
Read-Only Pane (embedded in task detail page):
- WS
/api/v1/runs/{id}/pane/ws - No input capability
- Always available (no lease enforcement)
- Purpose: Monitoring without interaction
Interactive Attach (run view page):
- WS
/api/v1/runs/{id}/attach/ws - Bidirectional PTY relay
- Single-writer lease (only one client at a time)
- Full terminal takeover
- Purpose: Interactive debugging and intervention
Visual Differentiation:
- Read-only pane: Smaller height, no input prompt visual cues
- Attach pane: Full-height, clear focus border, “Attached” badge
Accessibility Considerations
- Keyboard Navigation: All interactive elements keyboard-accessible (Tab, Enter, Space)
- Screen Reader Support: ARIA labels on dynamic content, live regions for terminal updates
- Color Contrast: WCAG AA compliant (dark/light mode support)
- Focus Management: Logical tab order, visible focus indicators
- Terminal Accessibility: xterm.js provides screen reader announcements for pane changes