PRD — Client Dashboard (ops, single-client operational dashboard)
Status: Draft, pending owner review · Authors: Franky + Claude · Date: 2026-07-27 Related: #4941 (three-layer analytics), #4963 (Business layer), #4959/#4961 (Dashboard IA), #4823 (org/OSA cost trends), #1699 (analytics org scoping)
The English version, client-dashboard-prd.md, is the spec of record; this document is its Chinese mirror.
1. Overview
The platform-level dashboard already exists at /ops/analytics (the SA landing page since #4961). Its four layers—Now / Loop / Diagnose / Business (+ cost ledger)—answer, "How is the platform operating as a whole?"
This PRD completes the client-level dashboard: select one client and inspect its operational state using the same layered language, supplemented with client-specific context unavailable in the platform view (Specialist roster, engagement information, and channel health). Core use cases: AM client reviews/QBR preparation, escalation diagnosis ("Is this problem limited to this client?"), and renewal negotiations.
2. Requirements Review (Corrections and Additions to the Original Requirements)
The original requirement—"a client-level dashboard with a client selector and Now → Loop → Diagnose → Business → Cost layers"—is directionally sound. The review produced three corrections/additions:
R1 — Consolidation requirement (most important). Client-level operational data is currently scattered across three places: /ops/clients/{id} (details), the Org Usage section of /ops/billing (workload/monthly cost/sessions/rate limits, with org selection), and the Client LLM Cost Trends section of /ops/billing. Adding a fourth entry point would recreate the fragmentation that #4959 just removed. Client Dashboard must absorb those two org-level analytics sections from /ops/billing (billing returns to pure billing records); /ops/clients/{id} retains its configuration/settings role and links to this page for operational status.
R2 — Layer semantics must be remapped, not copied verbatim. At single-client scope, some platform metrics change meaning or lose statistical validity:
- Loop metrics may have very small samples for a single org (for a client with only 12 tasks in 30 days, p95/independence rate is noise). Every cell must include
n; below the threshold (default n < 20), render a "low sample size" warning rather than implying false precision. - Client-level "Cost" should be a separate layer (the original requirement is correct to split "Business → Cost"): Business = commercial profile (subscription, rates, engagement); Cost = LLM spend trends (the #4823 series).
- Client-specific context (Specialist roster + thresholds, channel health, engagement dates) has no platform-layer equivalent; include it in the Business layer.
R3 — Roles. The platform dashboard centers on SAs; the primary user of the client dashboard is actually the AM. AMs can already view org-level workload/sessions/rate limits in billing. Rate and cost data is currently SA-only ("Can AMs see single-client costs?" remains an open question). Default: AMs see Now/Loop/Diagnose + non-monetary Business facts; Business rates + the Cost layer remain SA-only until the owner decides (OD-1).
3. Non-goals
- A client-facing dashboard (clients have their own portal; this page is an internal HP surface).
- Gross margin/Expert labor costs (#3196, open).
- Real-time push beyond the existing 30s
pollWhenVisiblepattern. - Cross-client comparison views (the platform dashboard owns that responsibility).
4. Entry Points and IA
- Route:
/ops/client-dashboard?org={orgId}—add a third item, "Client dashboard," to the sidebar Dashboard group (visible to AM + SA). No org selected → picker empty state (same pattern as billing Org Usage).?org=makes the view shareable/bookmarkable. - Direct access: "View dashboard" in the
/ops/clientsrow menu; a header button on/ops/clients/{id}. (Decision note: nested route/ops/clients/{id}/dashboardwas considered—it offers more natural breadcrumbs, but separates the entry point from the Dashboard navigation group and conflicts with the picker-first flow. Revisit if the picker-page experience is poor.) - Org picker: Same as
/ops/billing(SAs see all orgs; existing scope controls limit AMs to their own clients). - Time selector: 7/30/90d, top right, with semantics matching the platform page—flow metrics follow it; state metrics do not and explicitly identify their window (see R2/AC).
5. Layer Specifications
Scope note: every row marked (org) is filtered by the selected orgId; all are org-level aggregates available through existing ops read paths and do not cross the ADR-020 boundary.
Now — "Is this client on fire?"
| Metric | Data source | Backend status |
|---|---|---|
| Unhandled SLA breaches (org) | /analytics/summary + orgId | Must add ?orgId= (P2; reuse the same scoping pattern as #1699's AnalyticsCaller, which already exists on rollup endpoints) |
| Queue depth (pending/resolved within the period) (org) | Same as above | Same as above |
| p95 / average response latency (org) | Same as above | Same as above |
| Automatic resolution rate (org) | Same as above | Same as above |
Loop — "How well is the machine operating for this client?"
| Metric | Data source | Backend status |
|---|---|---|
| Scoreboard B (8 cells) (org) | /analytics/scoreboard-b + orgId | Must add ?orgId= (P2) |
| Honest low-sample reporting | Frontend | Show n in every cell; render a "low sample size" warning when n < 20 |
Diagnose — "What should we fix for this client?"
| Metric | Data source | Backend status |
|---|---|---|
| Improvement four-panel view (draft quality/feedback/KB misses/golden-pass) (org) | Existing rollup endpoints | Ready—endpoints already support ?orgId=/?specialistId=; only the frontend cards need to pass the parameter (P1) |
| Escalation reasons / confidence distribution (org) | /analytics/summary + orgId | Must add ?orgId= (P2) |
| Expert coverage for this client | Query expert_access by org (who reviews this client and their authorization scope) | Small new read path or filter parameter (P2) |
Business — "What is this client's commercial profile?"
| Metric | Data source | Backend status |
|---|---|---|
| Subscription (per-OSA rows, status, monthly rate) | Existing Lago ops overview filtered by org | Ready (P1) |
| Engagement information (start date, trial end, billing anchor day) | Org fields (same semantics as anchorDayFromOrg) | Ready (P1) |
| Specialist roster (OSA + primary assignment) | Existing org detail data | Ready (P1) |
| Session volume vs plan limit / rate limiting / workload | Migrated from /ops/billing Org Usage (R1) | Ready (P1) |
Channel health (latest send/receive per channel, recent channel_failures) | New lightweight read path | P3 |
Cost — "How much LLM cost has this client incurred for us?"
| Metric | Data source | Backend status |
|---|---|---|
| Daily trend / calendar month / billing cycle, org + per-OSA | #4823 getLlmCostTrends, getOrgLlmCost, osa-cycles | Ready—direct reuse (P1); migrate the Client LLM Cost Trends section from /ops/billing (R1) |
6. Phases
- P1 — Page shell + ready layers (zero backend changes). Route, picker, and time selector; Business + Cost layers; pass
orgIdto the improvement four-panel view; migrate the two org-level sections from /ops/billing. Deliver PRs split by section. - P2 — Org parameterization for summary + scoreboard. Add
?orgId=to/analytics/summaryand/analytics/scoreboard-b(reuse the #1699 scoping pattern and its "404 when out of scope" contract); activate the Now + Loop layers; add the Expert coverage read path. - P3 — Client-specific enhancements. Channel health; additional requirements from the first round of client-review feedback.
7. Open Decisions
- OD-1 (owner): AM visibility into monetary data—aggregate rates + Cost layer. This spec defaults to SA-only, with AMs seeing everything else. This is the longstanding "AM visibility into single-client costs" question; resolving it here offers value beyond this page.
- OD-2: Low-sample threshold (default n < 20), and whether low-sample cells should be dimmed or only labeled.
- OD-3: Whether
/ops/clients/{id}should embed a mini summary strip (a sparkline row linking to this page) or only provide a link.
8. Acceptance Criteria (P1)
- The sidebar Dashboard group includes "Client dashboard" (AM + SA); no org selected → picker empty state;
?org=preserves the selection. - After selecting an org: Business + Cost + improvement panels render org-level data; the time selector controls flow metrics; state metrics identify their window ("live" / "current cycle").
- /ops/billing no longer renders Org Usage or Client LLM Cost Trends; both move to this page; billing retains subscription/invoice/payment failure/cycle cost columns.
- AMs cannot see rate/cost data (OD-1 default); SAs have full visibility.
- Every new frontend section has specs that enforce org scoping + role gating (following the #4963 test pattern).
9. Risks
- 404-vs-403 contract: AM access to out-of-scope orgs must follow the existing cross-org read rules (404 when out of scope, #1472/#1699).
- Billing-page migration regression: The migrated sections have existing behavior (AM-visible subset, SA-only cards); migrate their tests with the code.
- Sample-size misinterpretation: Without a low-sample warning, single-client Loop figures will enter QBRs with false confidence—the warning is a P2 release gate, not a polish item.