Operant Studio
OPER-178

/qa/live-fixes canvas + /qa/triage-log page in Operant Studio

Band
MEDIUM
Suggested
Justin Cooke

Body

feature_id: FEAT-studio-qa

## Context

PDT memo v5 ([REDACTED-DOB]) approved two Studio UI surfaces to make the AI-triage auto-dispatch loop observable and manually overridable without becoming a required gate:

1. `/qa/live-fixes` — real-time canvas of in-flight auto-dispatched fixes. Each row shows Task id, fix_class, tier, dispatch time, current PR status, and a Cancel button. Not a gate — nobody has to watch. Anyone who chooses to can click Cancel on a specific row, which flips the ticket to AWAITING_HUMAN and writes an AiTriageLog kind=CANCEL row.

2. `/qa/triage-log` — filterable retrospective view of the AiTriageLog table (created in OPER-177). Filters by fix_class, tier, kind, actor, date range. This is the picture Justin or any team member checks when they want to see what auto-dispatch has been doing, review demoted classes, or investigate a specific incident. Replaces the Slack-channel notification originally proposed and rejected.

## Acceptance Criteria

- New page in `apps/web/src/app/qa/live-fixes/page.tsx` — server component reads Task rows where dispatch_mode=AUTO AND status IN (IN_PROGRESS, IN_REVIEW). Client component polls every 15s. Row columns: identifier, fix_class, tier (badge colored per tier), dispatched_at (relative time), current status, Cancel button.
- Cancel button calls existing endpoint from OPER-177: POST /api/studio/qa/live-fixes/:taskId/cancel. Optimistic UI update, error toast on failure. Only visible to members with team writer role (existing RBAC helper).
- New page in `apps/web/src/app/qa/triage-log/page.tsx` — filterable table over AiTriageLog. Server-side pagination (50 rows/page). Filters as URL params so views are shareable. Columns: created_at, kind (badge), fix_class, tier, task_id (link to ticket), test_run_id (link to run), actor, payload_json (collapsible detail row).
- Empty states for both pages: /qa/live-fixes shows 'No auto-dispatched fixes in flight' with link to /qa/triage-log for history. /qa/triage-log shows 'No triage events match your filters' with clear-filters button.
- No new backend queries — both pages read through existing Studio DB access patterns with getOrgScopedDb + Postgres RLS per multi-tenant-rls.md rules.
- Accessibility: keyboard navigable, screen reader labels on all badges and interactive elements. Cancel button has aria-label including the ticket identifier.
- Component tests for both pages covering: renders in empty state, renders with mock data, Cancel button gated to writer role, filter URL params round-trip correctly.

## Risk classification

Sandbox (Risk 3 / Reach 4). Internal-only UI, no PHI, no writes except the Cancel button (which delegates to OPER-177's audited endpoint). Happy-path tests + component tests sufficient per Build Risk Matrix.

## Depends on

- OPER-177 (AI triage writer) — creates the AiTriageClassPolicy + AiTriageLog tables + Cancel endpoint this UI reads from and calls.

## Blocks

- No downstream tickets.

## Splitting note

If the PR grows past the 800-line size budget (per pr-size-budget.md), split into two tickets: one for /qa/live-fixes canvas, one for /qa/triage-log page. Both are independent and shippable separately once OPER-177 lands.

Attachments

Loading attachments…

Comments

Loading comments…