# Agent page + /plans list rework (signed-off mockup round)

audience: AI coding agents first. Imperative, BLUF. Design reference for the agent page =
`scratchpad/agent-page-mockup-v3.html` (approved). Copy LAYOUT/HIERARCHY/COPY/BEHAVIOR; implement
in deck-ui idiom (tokens + Tailwind), NOT the mockup's CSS. Companion to (same hard rules as)
`docs/specs/2026-07-18-run-page-v2-spec.md`: no fabricated numbers; every element a named
`@overdeck/deck-ui` component; colocated tests + fixtures; both themes; honest labeled gap states.

## Scope

1. **Agent page** `/plans/[runId]/agents/[agentId]` — per-agent observability + intervention.
   Reached from run-page-v2's SegmentDetailDrawer "open agent" + attention rows.
2. **/plans list rework** (master-plan items 1, 2, 6, 7): two-pane viewport-fit, "Nine lives of
   one run" heading → "Runs", per-run/row timestamps, enriched row chips.
   OUT of scope: RunLanes geometry rework (items 3+4 — separate signed-off effort later).

## Data sources (verified)

REAL — wire it:
- `forensics:<runId>` panel segments carry `agentId`, `taskId`, `cat`, `t0`, `durMs` → agent's
  historical activity feed (group segments by agentId; TurnCards from segment sequence).
- Harness control-api `GET /runs/:id/timeline` via collector proxy → event detail per task.
- `steer` collector verb (free text, and `restart: true`) — Restart + composer are LIVE-wired.
- `plans` panel: full task nodes + `seq` (forwarded since run-page-v2 T3), `updatedAt`, `status`,
  `pendingDecisions`.
- Live refresh: collector SSE (~5s) via `useSseStream`. Live-vs-historical: agent is live iff its
  task `status === 'running'` in the plans panel.

BLOCKED on harness spec (`mega-plan-harness/docs/specs/2026-07-18-observability-instrumentation-and-control-api.md`) —
component built, honest gap state until it lands:
- B2 per-agent live stream → raw-log pane renders "live log needs harness B2" state; feed still
  shows segment/timeline history.
- B3 per-agent pause/kill → buttons disabled + tooltip "needs harness B3". Restart stays enabled
  (steer restart is real).
- A9 account slug → sidebar "account: not recorded (A9)" with tooltip.
- A10 watchdog idle signal → IdleTimer shows "idle signal needs A10"; NEVER a fake counting clock.
  When A10 lands: two-tier thresholds (Working → No progress at idleTimeoutMs → Watchdog
  recovering at ~2×; Ended when dead) on the watchdog's own clock.
- A8 costUsd → cost line hidden.

## Components (new, `packages/deck-ui/src/`, exported; reuse UndoToast/DeckTooltip/DataCoveragePanel/SectionCard)

| Component | Mockup ref | Notes |
|---|---|---|
| `AgentStatusBar` | top bar | identity (agent id, seat, task, model from seat binding), state dot + plain-language state line, controls IN the top bar, active-only: Restart (real, UndoToast 5s pending like abort), Pause/Kill disabled (B3); Kill keeps confirm dialog for when B3 lands |
| `AgentFeed` + `TurnCard` | friendlyFeed | chronological cards from segments/timeline: phase label, when + duration, what-happened line; raw-log toggle → B2 gap state; Live mode appends via SSE; Historical mode = static + "ended" banner |
| `IdleTimer` | stateDot area | A10-gapped (above); 1s tick ONLY when A10 data present |
| `AgentMetaSidebar` | right column | seat/model/attempt/branch/task link; account = A9 gap; cost hidden (A8) |
| `AgentComposer` | composerPanel | LIVE only (unmounted when historical — not disabled); sends via `steer` verb; fills area under feed, no bottom gap |
| `PlanRunListRow` | /plans list | compact row: title, status badge, `run #<seq>`, `N/M tasks`, relative last-activity (+ absolute on hover via DeckTooltip), active-agent count (from running tasks; NO max denominator — not in data); selected state; Open → `/plans/<runId>` |

Also: `formatRelativeTime(ms: number): string` in `packages/deck-ui/src/format.ts` (exported,
tested — "2d ago"/"4h ago"/"just now").

## /plans page layout (item 1+2)

`PlansContent.tsx`: two-pane grid filling viewport height (`min-h-0` + `overflow-y-auto` panes;
FIRST verify the shell provides a bounded height — if not, pane claims
`h-[calc(100vh-<header>)]`). Left: scrollable `PlanRunListRow` list. Right: selected plan's
forensics (existing tiles/PhaseBars/RunLanes/heading — unchanged EXCEPT the section title "Nine
lives of one run" → "Runs"; kill every nine-lives string repo-wide).

## Acceptance (page level)

- Gates green: deck-ui tests, web build + typecheck.
- Live app: `/plans` fits viewport + list scrolls + rows show seq/status/relative time;
  agent page renders for a real agentId from forensics segments; composer visible only on a
  running task's agent; Restart fires a real steer (verify collector 2xx); every blocked surface
  shows its labeled gap state; zero mockup demo literals in built output.
- Playwright (pattern: `scratchpad/verify-run-page-v2.mjs`): no-bottom-gap composer layout,
  live-vs-historical mode switch, disabled Pause/Kill tooltips, zero console errors, both themes.
