Add operator Agent index page

Task 12285 · Queued · Coder · created · updated

Epic 12243 (plan task 12246). Full settled inputs: the PRD (Task 12244) and the Architecture (Task 12245) of this epic — read them with `task_read`/`artifact_read` on those tasks; section numbers below (R…/AC…/§…) cite them. Where PRD and the original proposal (Task 12195) differ, the PRD governs. Reads the metrics child's agent-index API. ## Original requirement (verbatim) > "5. Surfaces (console UI style guide): Agents › Performance profile per agent/model/configuration; Compare 2–4 configurations on identical strata; org Leaderboard (filters persona, project, period, min n); task Outcome panel shows the ledger row and attribution links; Command center widgets read the ledger in place of the weekly sample; CSV export per org. Operator plane: Fleet › Agent index with cross-org aggregates per configuration, each cell ≥ 5 orgs and ≥ 100 outcomes, never a tenant name." (Settled: D4 show both — ledger widgets beside the unchanged `quality.avg`; D8 Owner/Admin only; Architecture A2 — the pages are tabs of the Command center, not under the grid-gated Agents section; the operator page is "Agent index" in the operator sidebar group.) > Done means (epic, verbatim): "Fleet › Agent index refuses a cell under 5 orgs or 100 outcomes." PRD requirement carried (verbatim): R5.7 "**Operator plane, Fleet › Agent index**: a new operator-only area behind `RequireSuperadmin` / `require_operator`. It shows cross-org aggregates per configuration. A cell with fewer than 5 orgs or fewer than 100 outcomes shows \"Below threshold\" and no number. No org or tenant name appears anywhere on the page, in its API response or in its export." ## Approach (settled — realize, do not reopen) Architecture §9.3 Operator Agent index: route `/operator/agent-index` behind `RequireSuperadmin` (no `orgAdmin`), sidebar entry **Agent index** in the operator group (`components/Sidebar.tsx`, `operatorOnly`) — no one-item "Fleet" group (style guide no-orphan-nav). Reads `GET /api/operator/agent-index` (metrics child); persona and period filters; "Below threshold" cells; totals labelled "over shown cells". Table scrolls in its own container at 390 px. Credentials & reach: - pytest ACs: none beyond the worktree (`./dc` stack) — verified (worktree). - Dev UI flows as Jaah-org Owner and as operator: `me/jaah/debug/main/operator-login` (operator, switches into tenants); environment `https://app.dev.jaah.ai` (public dev console; the internal `dev.jaah.ai` ALB did not answer from h2 within 15 s) — verified: credential_get ok; `app.dev.jaah.ai` → HTTP 200 from h2. - Member-seat negative checks (AC3.3, AC5.2): `org/console/dev-tpl-member-login`, dev — verified: credential_get ok. Out of scope: the threshold logic (server-side in the metrics child — never re-implemented in the UI); tenant pages. Out of scope (epic, verbatim): "any routing behaviour (the routing epic reads this ledger); new production signal sources (the production QA epic writes deployed/incident_linked); a public index or tenant-to-tenant sharing; re-scoring tasks older than a one-off backfill of merge, lane and cost facts." Do NOT touch (PRD A8): no change to `apps/api/app/agent_resolution.py` resolution order, `apps/api/app/jaah_scheduler.py` account placement, or `apps/api/app/jaah_routing/` (`routing_decision_id` stays null); no deploy ingestion, monitor adapters, incidents table or new trigger source kinds (`apps/api/app/triggers.py` untouched; `deployed_at`/`incident_linked` are columns/states nothing writes); no public page, no unauthenticated endpoint, no tenant-to-tenant view, no tenant name on any operator-plane aggregate; no retired or rewritten weekly outcome-review job (`apps/api/app/operator_dashboard/review.py`, `apps/api/app/operator_dashboard/quality.py`, D4); backfill writes merge, lane and cost facts only, never re-runs LLM scoring, never re-opens old tasks; benchmark mode never merges a shadow PR and never changes the origin task's dispatch. No CP forge call (ADR-0156): forge facts come only through the `outcome_scan` runner. Migrations additive only (new tables + nullable/defaulted columns). Done means: - AC5.8 "As the operator, open Fleet › Agent index [operator sidebar › Agent index]. Observe aggregate cells. A cell under 5 orgs or 100 outcomes reads \"Below threshold\". Search the page text and the API JSON for any org name and find none." - A non-operator (Owner of a tenant) gets the no-access page at the direct URL and no sidebar entry. - AC5.9 (page) "At a 390 px viewport, every page above has no horizontal page scroll. Tables scroll inside their own container." - vitest covers the below-threshold cell rendering (no number) and the operator-only nav entry. - Gate (epic Done means, verbatim: "Required checks: ./dc check quick green; changed-area tests green; merge lane full gate green.") — `./dc check quick` green; changed-area pytest/vitest green; merge lane `./dc check full` green; Jaah Deploy of the PR succeeds.

Activity

  • Draft
  • Ready