# Feature Spec: Dashboard Advanced UX - Second Pass (AOP)

> Purpose: define the M45 dashboard wave with low-cognitive-load UX, strict implementation contracts, and deterministic behavior for agentic execution.

**Version:** 2.0 (rewritten for execution quality)
**Date:** 2026-03-05
**Status:** Completed
**Roadmap Mapping:** M45
**Depends On:** M44 (UX-01..UX-20 complete)

---

## 0. Scope and Standards

### 0.1 Primary Users and Jobs

This spec optimizes for two primary jobs:

1. **Reviewer job:** make correct approve/deny/request-changes decisions quickly.
2. **Operator job:** detect stalled runs, lock contention, and systemic issues before throughput drops.

Secondary job:

3. **Author job (in-scope for M45):** create a new feature run from dashboard with server-side validation and policy checks.

### 0.2 Critical UX Corrections from v1.0

The previous draft over-indexed on "more widgets" and under-specified decision flow. This rewrite makes these corrections:

| v1.0 Issue                            | Why It Is Problematic                         | v2.0 Correction                                                                    |
| ------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------- |
| Dense feature packing in one screen   | High cognitive load, weak scan hierarchy      | Progressive disclosure + clear view boundaries (Board vs Analytics vs Focus route) |
| Heuristic language like "Likely Met"  | Creates false confidence                      | Rename to **Verification Signals** with explicit confidence labels and caveats     |
| Color-first status encoding           | Accessibility failures for color-vision users | Every status includes icon + text + semantic label                                 |
| Right-click-only action affordances   | Not keyboard/touch accessible                 | Explicit action menu button + full keyboard path                                   |
| Unbounded client polling/render loops | Performance instability at scale              | Explicit budgets, throttling, and update intervals                                 |
| `POST /api/run` shell ambiguity       | Security and reliability risk                 | Contract requires orchestrator tool path; shell-out disallowed in default path     |
| Missing API error contracts           | Agent implementation ambiguity                | Uniform response envelope and explicit error codes                                 |

### 0.3 UX Principles (Mandatory)

1. **Task-first hierarchy:** surface what helps decisions first, details second.
2. **Progressive disclosure:** default collapsed for deep diagnostics.
3. **Truth over optimism:** inferred states must be labeled as inferred.
4. **Accessibility by default:** keyboard complete, non-color semantics, ARIA labels.
5. **Operational safety:** destructive or high-impact actions require guardrails and confirmation.
6. **Determinism for agents:** every feature includes explicit data source, fallback, and testability.

### 0.4 Required Quality Standards (Mandatory)

All M45 implementations MUST satisfy:

- TypeScript strict mode passes.
- ESLint zero warnings.
- Coverage >= 90% lines/branches/functions/statements.
- `npm run build` passes for workspace and dashboard build target.
- Existing API contracts are preserved or versioned.
- Accessibility: WCAG 2.2 AA baseline for interactive components.
- No critical interaction depends on pointer-only behavior.

### 0.5 Non-Functional Budgets (Mandatory)

- SSE update handling SHOULD complete in < 50ms per snapshot for 200 features on a dev laptop baseline.
- No component may run `setInterval(1000)` unless required by human-visible countdown; prefer 5s cadence for low-value timers.
- Large lists/tables (>150 rows) MUST use virtualization or pagination.
- API errors MUST render non-blocking inline states (no white-screen failures).
- Responsive baseline is mandatory: dashboard core flows MUST be fully usable at **360px CSS width** in portrait orientation (phone-class viewport), including triage, feature selection, detail inspection, and review actions.
- At the phone baseline, no horizontal page-level scrolling is allowed for primary workflows; controls must remain reachable by keyboard and touch.

---

## 1. Experience Architecture

### 1.1 Views and Information Scent

M45 uses three intentional contexts:

1. **Board (`/`)**
   - For triage and workflow control.
   - Shows columns, summary metrics, runtime health, lock/dependency summaries, and selected-feature detail panel.
2. **Analytics (`/analytics`)**
   - For trend and provider/collision insights.
   - Keeps heavy tables/charts out of triage view.
3. **Focus (`/feature/:id`)**
   - For deep review of one feature.
   - Full-width stacked detail sections.

### 1.2 URL-State Contract

To reduce user disorientation and allow shareable links:

- Board selection state MUST sync to URL query (`?feature=<id>&filters=...`).
- View mode MUST be route-driven (not only local state).
- Browser back/forward MUST restore selected feature + filters.

### 1.3 Progressive Disclosure Rules

- Default expanded: summary, current phase status, review actions.
- Default collapsed: collision matrix, raw evidence, live feed details, revision metadata.
- In Focus view, reviewer-critical sections appear before diagnostics.

---

## 2. Revised Improvements (UX-21 to UX-40)

### UX-21 - Agent Pipeline Stepper

**Intent:** reveal where a feature currently sits in planner -> builder -> qa.

**Requirements:**

- Source from `feature.role_status`.
- Render stepper with icon + text state: `Ready`, `Running`, `Blocked`, `Done`, `Unknown`.
- Running step uses motion + text, never motion-only.
- If role status is missing, render `Unknown` with neutral styling.
- Update via SSE snapshots; no extra API call.

### UX-22 - Per-Feature Cost and Token Tracker

**Intent:** make cost visibility immediate without noisy calls.

**Requirements:**

- API route: `GET /api/features/:id/cost`.
- Fetch only when feature detail is open; cache per feature for 60s.
- Use `AbortController` when switching selected feature.
- Render `Tokens`, `Estimated Cost`, and `Last Recorded`.
- Summary tile uses aggregated value from server payload (not N feature calls).
- Empty state: `No cost data recorded yet`.

### UX-23 - Plan Scope File Tree

**Intent:** communicate blast radius faster than flat lists.

**Requirements:**

- Parse `plan.files.create|modify|delete` into tree model.
- Render operation badges (`Created`, `Modified`, `Deleted`) with icon + text.
- Collapse depth > 2 by default.
- For >200 nodes, use virtualization or chunked rendering.
- Show `allowed_areas` and `forbidden_areas` as separate policy chips.

### UX-24 - Acceptance Criteria Verification Signals

**Intent:** show progress signals without overstating certainty.

**Requirements:**

- Rename section to **Verification Signals**.
- Never label inferred criteria as fully met.
- Status values:
  - `Verified` (direct artifact evidence)
  - `At Risk` (explicit gate/evidence failure)
  - `Unverified` (insufficient evidence)
- Each item includes `reason` and `evidence_ref` when available.
- Summary line: `X verified, Y at risk, Z unverified`.

### UX-25 - QA Test Coverage Map

**Intent:** expose test accountability per changed file.

**Requirements:**

- API route: `GET /api/features/:id/test-index`.
- Visible in `qa` and `ready_to_merge`.
- Table columns: file path, status, required test count, last run.
- Status labels must include icon + text.
- Filter pills: `failed`, `pending/running`, `waived`, `passed`.
- Empty state if artifact missing.

### UX-26 - Lock Resource Map

**Intent:** make lock contention diagnosable in one glance.

**Requirements:**

- Extend status payload with normalized lock entries.
- Row fields: resource, holder, expires in, stale flag.
- Countdown refresh every 5s.
- Stale rows have text badge `Stale lease`.
- Clicking holder selects that feature.

### UX-27 - Dependency Unblock Chain

**Intent:** expose blocked critical path clearly.

**Requirements:**

- Source from `dep_blocked` in index/status payload.
- Feature card shows `Awaiting Dependencies` substate when applicable.
- Detail panel lists unresolved dependencies as navigable links.
- Board section `Dependency Chains` shows `feature -> depends on -> feature` rows.
- Rows disappear automatically on next snapshot when resolved.

### UX-28 - Review Brief Renderer

**Intent:** present generated review context as first-class reviewer input.

**Requirements:**

- API route: `GET /api/features/:id/review-brief`.
- Render in `ready_to_merge`; optional in `qa`.
- Sections: intent, scope, contract risk, feasibility, gate matrix, unresolved questions, evidence references.
- Show `generated_at` + freshness age.
- Missing brief state: `Review brief not generated yet`.

### UX-29 - Plan Risk Annotations

**Intent:** keep planner-declared risks visible at decision time.

**Requirements:**

- Read from `plan.risk`.
- In `ready_to_merge`, section is expanded by default.
- Support optional severity prefix parsing (`[high]`, `[medium]`, `[low]`); default `medium`.
- Risks render as callouts with severity text badges.

### UX-30 - Gate Step Drill-Down

**Intent:** shorten time-to-diagnosis for gate failures.

**Requirements:**

- Parse gate evidence JSON using tolerant parser with shape guards.
- Render per-step: name, command, status, exit code, duration.
- First failed step expands by default.
- Output tail is collapsible, capped to last 50 lines.
- Unknown artifact shape falls back to raw JSON viewer.

### UX-31 - Orchestrator Run Health Panel

**Intent:** expose run liveness and lease risk early.

**Requirements:**

- Include runtime session in status payload.
- Show provider, model, uptime, heartbeat age, lease countdown.
- Health states:
  - `Healthy` (<30s heartbeat)
  - `Degraded` (30s-2m)
  - `Critical` (>2m)
- No active runtime: show `No active orchestrator run`.

### UX-32 - Throughput Sparkline

**Intent:** provide fast trend check without analytics overload in Board view.

**Requirements:**

- Compute 14-day merges histogram from merged feature timestamps.
- Render compact SVG sparkline with tooltips.
- Board shows tiny sparkline tile + link to full analytics route.

### UX-33 - Provider Performance Analytics

**Intent:** support provider/model tradeoff decisions.

**Requirements:**

- API route: `GET /api/analytics`.
- Place in `/analytics` route.
- Table columns: provider, model, features, success rate, avg cost, avg duration, avg retries.
- Sortable headers with accessible sort state labels.
- Include recent outcomes timeline (last 20).
- Refresh interval 30s, pause when tab hidden.

### UX-34 - Collision Matrix Heatmap

**Intent:** make collision topology understandable.

**Requirements:**

- API route: `GET /api/collisions`.
- Render matrix for <=50 active features.
- For >50 features, degrade to ranked collision pair list.
- Cell tooltip includes collision type and count.
- Use text legend + color legend.

### UX-35 - Plan Revision History

**Intent:** provide context for scope churn.

**Requirements:**

- Surface `plan_version`, `revision_of`, `revision_reason`.
- Display as a compact revision timeline card.
- If historical artifacts are not available, explicitly label: `Only current plan metadata is retained`.

### UX-36 - Live Cross-Feature Event Feed

**Intent:** provide stream-level observability without noise overload.

**Requirements:**

- Compute feed events by diffing SSE snapshots.
- Event types: phase, gate, activity, pr, lock.
- De-duplicate repeated identical events within 10s window.
- Keep last 100 events.
- Provide pause/resume and clear actions.
- Collapsed by default.

### UX-37 - Quick Launch Panel

**Intent:** provide in-dashboard feature launch for authorized operators in M45.

**Requirements:**

- Feature flagged: `dashboard.quick_launch`.
- API route: `POST /api/run`.
- Default path MUST call orchestrator tools (not shelling out).
- Validate `feature_id` pattern and required goal.
- Require explicit confirmation before launch.
- If disabled by policy or permission, hide control and return authorization error on direct call.

### UX-38 - Flaky Test Indicator

**Intent:** distinguish probable flaky failures from likely regressions.

**Requirements:**

- API route: `GET /api/flaky`.
- If data unavailable, omit indicator without error toast.
- Show indicator only when failing required tests overlap flaky suspects.
- Label as probabilistic signal, not definitive cause.

### UX-39 - Feature Budget Meter

**Intent:** prevent late surprise budget blocks.

**Requirements:**

- API route: `GET /api/policy/budget`.
- Meter compares `estimated_cost_usd` to per-feature limit when configured.
- States: `Normal` (<60%), `Warning` (60-85%), `Critical` (>85%), `Exceeded`.
- `paused_budget` phase/status must show explicit `Budget exhausted` banner.

### UX-40 - Multi-View Layout Toggle (Route-Native)

**Intent:** support triage and deep review without cramped hybrid layouts.

**Requirements:**

- Views are route-native:
  - Board: `/`
  - List: `/?view=list`
  - Focus: `/feature/:id`
- List view supports sortable columns and keyboard row navigation.
- Right-click-only interactions are prohibited; action menu button required.
- Focus view includes full-width review sections with sticky action footer.

---

## 3. API Contracts (Revised)

### 3.1 Uniform Response Envelope (Mandatory)

All new dashboard routes MUST return:

- Success:
  ```json
  { "ok": true, "data": {}, "meta": { "stale": false, "source": "tool|artifact|derived" } }
  ```
- Error:
  ```json
  { "ok": false, "error": { "code": "string", "message": "string", "retryable": false } }
  ```

### 3.2 Required Routes

| Route                            | Method | Purpose                             | UX    |
| -------------------------------- | ------ | ----------------------------------- | ----- |
| `/api/features/:id/cost`         | GET    | Per-feature cost and tokens         | UX-22 |
| `/api/features/:id/test-index`   | GET    | Per-feature QA test index           | UX-25 |
| `/api/features/:id/review-brief` | GET    | Renderable review brief             | UX-28 |
| `/api/analytics`                 | GET    | Provider/model analytics            | UX-33 |
| `/api/collisions`                | GET    | Collision matrix or ranked list     | UX-34 |
| `/api/flaky`                     | GET    | Flaky suspects/quarantine metadata  | UX-38 |
| `/api/policy/budget`             | GET    | Budget policy thresholds            | UX-39 |
| `/api/run`                       | POST   | Quick launch entry point (in-scope) | UX-37 |

### 3.3 API Error Codes (Minimum Set)

- `feature_not_found`
- `artifact_missing`
- `tool_unavailable`
- `tool_timeout`
- `policy_not_configured`
- `unauthorized_action`
- `invalid_input`

---

## 4. Type Additions (`packages/web-dashboard/src/lib/types.ts`)

```typescript
export type RoleStatus = 'ready' | 'running' | 'blocked' | 'done' | 'unknown';

export interface AgentPipelineStatus {
  planner: RoleStatus;
  builder: RoleStatus;
  qa: RoleStatus;
}

export interface CostSummary {
  feature_id: string;
  tokens_used: number;
  estimated_cost_usd: number;
  recorded_at: string | null;
}

export interface QaTestIndexItem {
  path: string;
  status: 'pending' | 'running' | 'passed' | 'failed' | 'waived';
  required_tests: string[];
  last_run_at?: string;
}

export interface QaTestIndex {
  feature_id: string;
  version: number;
  items: QaTestIndexItem[];
}

export interface LockLease {
  resource: string;
  holder: string | null;
  expires_at: string | null;
  is_stale: boolean;
}

export interface ReviewBrief {
  intent_summary: string;
  scope_summary: string;
  contract_risk_summary: string;
  feasibility_score?: number;
  feasibility_breakdown?: Record<string, number>;
  gate_matrix: Record<string, string>;
  unresolved_questions: string[];
  evidence_refs: string[];
  generated_at: string;
}

export interface ProviderAnalytics {
  provider: string;
  model: string;
  total_features: number;
  success_count: number;
  success_rate: number;
  avg_retry_count: number;
  avg_duration_ms: number;
  avg_cost_usd: number;
}

export interface FeedEvent {
  id: string;
  timestamp: string;
  feature_id: string;
  type: 'phase_change' | 'gate_update' | 'activity_change' | 'pr_update' | 'lock_change';
  message: string;
  severity: 'info' | 'success' | 'warning' | 'error';
}
```

`FeatureSummary` additions:

- `role_status?: AgentPipelineStatus`
- `gate_retry_count?: number`
- `last_retry_at?: string | null`

`DashboardStatusPayload` additions:

- `runtime?: RuntimeSession`
- `lock_map?: LockLease[]`
- `dep_blocked?: Array<{ feature_id: string; depends_on_unresolved: string[] }>`
- `flaky_suspects?: string[]`
- `metrics?: { total_cost_today_usd?: number; merge_histogram_14d?: number[] }`

---

## 5. Agentic Implementation Plan

### 5.1 Delivery Slices

Execution priority for M45 is explicitly reviewer-first, then operator flow.

1. **Slice A - Reviewer Core**
   - UX-21, UX-23, UX-24, UX-29, UX-30, UX-35
2. **Slice B - Operator Core**
   - UX-26, UX-27, UX-31, UX-36, UX-39
3. **Slice C - Analytics**
   - UX-32, UX-33, UX-34, UX-38
4. **Slice D - Launch + View Refinement**
   - UX-37, UX-40, List/Focus routing polish

### 5.2 Required File Targets

- `packages/web-dashboard/src/app/page.tsx`
- `packages/web-dashboard/src/app/analytics/page.tsx` (new)
- `packages/web-dashboard/src/app/feature/[id]/page.tsx` (new)
- `packages/web-dashboard/src/components/*` (new/updated per feature)
- `packages/web-dashboard/src/lib/types.ts`
- `packages/web-dashboard/src/lib/dashboard-utils.ts`
- `packages/web-dashboard/src/lib/aop-client.ts`
- `packages/web-dashboard/src/lib/orchestrator-tools.ts`
- `packages/web-dashboard/src/app/api/**/route.ts` (new routes above)
- `apps/control-plane/test/dashboard-*.spec.ts` (API + utils + interaction tests)

### 5.3 Deterministic Fallback Rules

- Missing artifact: return `ok: false`, `error.code = artifact_missing` and render empty state.
- Missing tool support: `tool_unavailable`; UI renders feature section disabled with explanation.
- Any optional section failure MUST NOT block board rendering.

### 5.4 Security and Safety Rules

- `POST /api/run` MUST validate inputs server-side, regardless of client validation.
- Quick launch MUST honor policy + permission checks.
- Role-based authorization enforcement is deferred for M45, but route handlers MUST call a pluggable authorization adapter interface so RBAC can be enabled without route rewrites in a later milestone.
- Action confirmations required for launch/approve/deny/request changes.

---

## 6. Acceptance Criteria

M45 is accepted only when all are true:

1. Functional criteria
   - UX-21..UX-40 implemented according to this spec and degradations defined.
2. Accessibility criteria
   - Keyboard-only flow covers triage, feature selection, view switch, and actions.
   - No status semantics rely on color alone.
3. Reliability criteria
   - Optional section failures do not break board rendering.
   - API routes return uniform response envelope.
4. Performance criteria
   - No continuous high-frequency loops without justification.
   - Event feed de-duplication and bounded memory confirmed.
5. Engineering criteria
   - `npm run lint`, `npm run typecheck`, `npm test`, `npm run build`, `npm run validate:mcp-contracts`, `npm run validate:architecture` all pass.

---

## 7. Out of Scope

- In-dashboard code editing or patch authoring.
- Full mobile-first redesign beyond the mandatory 360px phone baseline.
- Websocket migration (SSE remains transport for M45).
- External alerting integrations (Slack/PagerDuty/webhooks).

---

## 8. Open Questions (Need Product/Governance Input)

None for this revision.
