## Context The console already has five required first-party areas, typed Platform API clients, role-scoped routes, long-running job polling, operation tracking, and the black-mecha/magical-girl theme system. The remaining problem is cohesion and truthfulness: the overview hides some API failures as empty collections, page mutations use inconsistent busy/confirm/retry behavior, and operators lack a compact cross-page view of active and failed work. This change stays inside `platform_web/` and consumes existing Platform API projections. It does not make the browser a source of truth for jobs, resource state, permissions, or secrets. ## Goals / Non-Goals **Goals:** - Make loading, empty, unavailable, stale, active, succeeded, and failed states visually and semantically distinct. - Give operators an actionable overview of server health, Run endpoint safe status, active/failed jobs, AI provider availability, resource pressure, and recent audit signals. - Standardize mutation behavior around permission gating, confirmation where state is disruptive, action-level busy state, persisted response handling, and retry after failure. - Keep first-party list pages full-width and dense while retaining modal/detail-route workflows. - Preserve the theme and responsive contracts at desktop and 390px. - Recover narrow-screen vertical space by moving the existing ordered text navigation into an off-canvas sidebar with a small theme-aware left-edge handle and bounded swipe gesture. **Non-Goals:** - No new Platform or Run endpoint, persistence model, permission, or job state machine. - No raw secret/key/token/path/PID/socket/credential/DSN/RCON projection. - No billing, cloud hosting sales, provider marketplace, fleet orchestration, arbitrary shell, or direct browser-to-Run channel. - No route, sidebar order, theme palette, or global particle architecture replacement. ## Decisions ### Decision 1: Aggregate existing safe APIs in the browser without inventing platform health The overview will fetch server instances, safe Run endpoint projections, jobs, metrics, platform usage, AI providers, and audit events as independent modules. Each module keeps its own state and refresh timestamp. Failed modules remain visibly unavailable and are excluded from healthy/empty conclusions. Alternative considered: add one new dashboard API. Rejected because current APIs already provide the bounded projections needed, and this change does not need a new backend contract. ### Decision 2: Derive operational summaries in shared pure contracts Job buckets, attention signals, server list sorting/filtering, provider summaries, and safe operation labels will live outside page components. Pages render derived view contracts and tests can lock classification without constructing full page trees. Alternative considered: calculate every summary inline. Rejected because the same terminal/active/failure classification is needed by the overview, server page, and global operation tray. ### Decision 3: Platform responses remain authoritative Mutations only update resource state from a successful Platform response or a subsequent refresh. Long-running operations use real job IDs and job polling. A rejected request keeps the previous resource state, preserves the failure message, and exposes retry where the same request remains valid. Alternative considered: optimistic state transitions. Rejected because plugin/provider/server operations can be rejected by ownership, lifecycle, dependency, or reference constraints. ### Decision 4: Disruptive state changes use a shared confirmation contract Plugin install/enable/disable, AI provider enable/disable/retire, user deactivation, and server destructive actions require resource-specific confirmation. Dialogs close on Escape or cancellation, preserve focus behavior, and disable duplicate submission while busy. Alternative considered: confirm only destructive deletes. Rejected because enable/disable and install actions can interrupt running capabilities or create durable work. ### Decision 5: Capabilities control commands, routes control discovery The existing route capability mapping remains the navigation authority. Within an allowed page, mutation controls check the matching session capability and render a clear read-only state when missing. The browser does not elevate access and still handles Platform 401/403 as authoritative rejection. Alternative considered: hide every unauthorized control. Rejected because operators benefit from understanding why a page is read-only; safety-critical commands remain unavailable. ### Decision 6: Extend the existing theme system with shared operational primitives New pulse rows, module status headers, compact job rows, and the operation tray use shared classes appended to `theme/base.css`, `var(...)` tokens, existing radius limits, and theme-specific material variables. Global decoration remains exclusively in `MagicalParticleLayer`. Alternative considered: page-local cards and effects. Rejected because they would fragment theme switching and violate the repository style contract. ### Decision 7: Use an off-canvas text sidebar on narrow screens At 760px and below, the sidebar leaves normal document flow and stays closed until the operator activates the left-edge handle or completes a rightward swipe that begins at the viewport edge. The drawer preserves the desktop route order, icons, Chinese labels, account access, and session operations; backdrop click, Escape, route selection, and a leftward drawer swipe close it. Alternative considered: keep the horizontal icon rail and account strip above every page. Rejected because it consumes scarce vertical space, hides route labels, and makes repeated mobile operations slower to scan. ## Risks / Trade-offs - [Risk] Independent overview requests can complete out of order. Mitigation: refresh callbacks update only their module and use explicit loading/ready/error states. - [Risk] More status rows can overload narrow layouts. Mitigation: collapse multi-column regions to a single ordered flow at 390px and keep command rows bounded. - [Risk] Horizontal content gestures can open navigation accidentally. Mitigation: require the opening swipe to start within the left 28px edge and travel at least 56px, while drawer-closing swipes use the inverse threshold. - [Risk] In-session operation history is not durable. Mitigation: label it as current-session request feedback; durable job and audit history continues to come from Platform APIs. - [Risk] Existing item 8 edits overlap `base.css` and API types. Mitigation: reuse its current worktree state, append narrowly scoped classes, and avoid reverting or rewriting lifecycle code. - [Risk] Action confirmation can add friction. Mitigation: require it only for persistent or disruptive operations; refresh and navigation remain immediate. ## Migration Plan 1. Add shared derived contracts and tests. 2. Add shared operation tray and accessible dialog behavior. 3. Update each first-party page while keeping existing API methods and routes. 4. Add shared theme styles and responsive rules. 5. Run frontend tests/typecheck/build, structure validation, strict OpenSpec validation, and browser acceptance in both themes and at 390px. Rollback is frontend-only: remove the new shared components/contracts and restore the previous page render paths. No persisted data migration is required. ## Open Questions - Resolved: this change uses existing APIs and does not add a dashboard endpoint. - Resolved: the operation tray is session-scoped feedback; Platform jobs and audit events remain durable truth. - Resolved: development fixtures may remain in tests, but normal page rendering does not silently substitute them for failed APIs.