# Agent activity story — request

audience: AI coding agents first.

status: DONE
task IDs: #3, #4, #5, #6, #7, #8, #9, #10

**Goal:** Ship a human-readable clicked-agent activity story with expandable routine work, accessible event diagnostics, and exact inline side-by-side diffs recovered from preserved work.

**Context:**

- Design authority: `docs/specs/2026-08-14-agent-activity-story-design.md`.
- Current page: `apps/web/src/components/plans/AgentApp.tsx` projects raw `HarnessEvent` records into technical `AgentTurn` rows.
- Current feed: `packages/deck-ui/src/AgentFeed.tsx` and `packages/deck-ui/src/TurnCard.tsx` expose phase/event vocabulary and raw logs.
- Reuse `packages/deck-ui/src/DetailDrawer.tsx`; NEVER create another inspector/drawer primitive.
- Current factory evidence: `collector/src/adapters/factory.ts` exposes `FactoryPhaseDiffView` with `phaseId`, numeric attempt, file summaries, `diffText`, truncation, and creation time. It does not prove agent-attempt linkage yet.
- Recover `DiffViewer` candidate from `/home/user/Projects/overdeck/temp-user/project-onboarding/overdeck-project-control-implementation/files/packages/deck-ui/src/components/DiffViewer/`; NEVER apply submitted 525-path patch or copy stale barrel.
- Preserved candidate hashes MUST match before recovery:
  - `DiffViewer.tsx`: `5ed4046d29a55e95220500a3ab44a626638c7e8a1c9c5130f805461d1e2c178a`
  - `DiffViewer.css`: `ab6f99f4fcdc86718bd3e616041a8160b83596f10490ad5a60878fd9656af001`
  - `DiffViewer.test.tsx`: `98037ba68c389eb1dcc66b7264442454aa691df995ad64e2c1590911f3e56bd7`

**Files:**

- Create `apps/web/src/components/plans/agent-activity-projection.ts` — map authoritative transport/evidence records into presentation-only story items.
- Create colocated projection tests under `apps/web/src/components/plans/` — vocabulary, grouping, ordering, deduplication, caps, and evidence linkage.
- Modify `apps/web/src/components/plans/AgentApp.tsx` — query/link authoritative diff evidence, own selection state, and pass presentation callbacks.
- Modify `apps/web/src/components/plans/AgentApp.test.tsx` — story, diagnostics, diff, correlation, and failure-state integration coverage.
- Modify `packages/deck-ui/src/AgentFeed.tsx` — story-first feed accepting typed presentation items.
- Modify `packages/deck-ui/src/TurnCard.tsx`, or replace it with `packages/deck-ui/src/AgentActivityRow.tsx` only when tests prove `TurnCard` cannot express grouped/edit rows — render accessible typed rows.
- Recover and reconcile `packages/deck-ui/src/DiffViewer.tsx` plus colocated test/style files following current package layout — reusable patch parser and split/unified viewer.
- Modify `packages/deck-ui/src/index.ts` — surgical exports only.
- Modify `apps/web/src/components/design-system/gallery-registry.tsx` and gallery tests — register all meaningful recovered viewer/story states.
- Modify `collector/src/adapters/factory.ts` and focused tests — add typed authoritative attempt/phase linkage needed by agent detail.
- Modify harness trace schema/writer only if current storage cannot supply explicit linkage. If touched, read `modules/harness/CLAUDE.md` and run full mandatory factory suite.

**Contract:**

```ts
type AgentActivityItem =
  | AgentProgressItem
  | AgentGroupItem
  | AgentEditItem
  | AgentTestItem
  | AgentDecisionItem
  | AgentWarningItem
  | AgentOutcomeItem

interface AgentActivityBase {
  id: string
  at: number
  title: string
  summary?: string
  diagnosticEventIds: string[]
}

interface AgentEditItem extends AgentActivityBase {
  kind: 'edit'
  path: string
  insertions: number | null
  deletions: number | null
  diffText: string | null
  truncated: boolean
  evidenceLevel: 'event' | 'phase'
}
```

- `agent-activity-projection.ts` owns one explicit vocabulary registry. Each entry pins source event kind, required payload fields, title formatter, grouping category, severity, and preserve/group policy.
- Group ID: `${attemptId}:${category}:${firstEventId}`. It MUST remain stable while live events append.
- Group only adjacent routine reads, searches, and non-mutating commands with identical authoritative run/task/attempt/category identity.
- NEVER group edits, mutation, tests/gates, decisions, warnings/errors, controls, blockers, or completion.
- Sort by parsed timestamp then authoritative source order. Exact duplicate IDs deduplicate. Conflicting duplicate IDs keep first record and expose diagnostics warning.
- Unknown event kind: title `Recorded activity`, severity `info`, category `other`; use available human-authored `summary`/`text`; NEVER use raw JSON as title.

```ts
interface DiffParseResult {
  files: DiffFile[]
  warnings: DiffParseWarning[]
}

interface DiffViewerProps {
  diff?: string
  files?: DiffFile[]
  selectedPath?: string
  defaultView?: 'split' | 'unified'
  allowViewToggle?: boolean
  collapseContextAfter?: number
  ariaLabel?: string
  parseWarnings?: DiffParseWarning[]
  onFileChange?: (file: DiffFile) => void
}
```

- `DiffViewer` supports multi-file, add/delete/modify/rename, split/unified, selected path, context collapse, and empty state.
- Binary, mode-only, submodule, rename-only, quoted-path, missing-newline, malformed-hunk, and truncated input produce structured visible warnings. Unsupported evidence MUST NOT look complete.
- Full status words and non-color indicators required. Tables require semantic old/new line and code headers.

Authoritative join chain:

```text
runId/adwId → taskId → attemptId → phaseId → path
```

- Add explicit typed collector linkage before attaching any phase diff to an agent.
- NEVER join by timestamps, numeric attempt coincidence, or assumed matching IDs.
- Missing/conflicting join fields fail closed: attach no diff and show coverage warning.
- Phase-level evidence label MUST be `Phase changes`; NEVER describe it as exact per-edit evidence.
- Exact event-level edit requires authoritative event/edit-operation linkage.

Presentation callbacks:

```ts
AgentFeed({
  items: AgentActivityItem[]
  live: boolean
  onOpenDiagnostics(itemId: string): void
  onToggleGroup(itemId: string): void
}): JSX.Element
```

- `packages/deck-ui` MUST NOT import transport types, resolve event IDs, or query collector data.
- Every row opens diagnostics through browser context menu, `Shift+F10`, Context Menu key, and visible control using one callback.
- Header `Diagnostics` opens agent-level identity/task/attempt/connection/coverage details; it MUST NOT silently select an event.
- Reuse `DetailDrawer` with `modal={false}` in reserved wide layout and modal default on narrow screens. Opening moves focus inside; Escape closes; close restores exact trigger or feed heading.
- Commands, input, output, IDs, and payloads render as escaped inert text after collector redaction. Never interpret HTML, ANSI, terminal escapes, or executable links. Bound each section to 24 KiB and label truncation.
- Group/file expanders expose `aria-expanded` and `aria-controls`; expansion never steals focus.
- Stream live region announces state changes, warnings, failures, and completion only. Respect reduced motion.

**Behavior:**

- Clicking any agent opens task goal, current state, elapsed duration, current work, and supported essential controls above chronological story.
- Default story MUST NOT permanently show model, account, IDs, branch, raw timestamps, event/run-offset tables, empty technical cards, or raw payload blocks.
- Routine groups say `See N actions` and expand chronologically.
- Every authoritative file row says `Edited (path)` and provides `See more`; expansion lazy-mounts inline split diff and toggles to `Show less`.
- Missing diff says `Diff was not recorded for this edit.` Truncated evidence shows coverage warning before viewer.
- Event cap shows `Earlier activity is not loaded`. Use supported continuation when available; otherwise state older records are unavailable.
- Wide inspector MUST NOT cover story. Narrow inspector uses full-width modal behavior. Diff owns horizontal scrolling; body never scrolls horizontally.
- Collector/SSE/malformed payload failures preserve received story and report honest retry/coverage state. No fabricated values or correlations.

**Out of scope:**

- Do not apply the broad preserved onboarding patch.
- Do not rebuild `DiffViewer` when preserved behavior can be reconciled.
- Do not create a new drawer, table, chip, menu, tooltip, or other generic primitive.
- Do not redesign `/factory`, `/logs`, `/agents`, or unrelated pages.
- Do not change trace storage except exact fields required for authoritative agent-attempt/phase linkage.
- Do not synthesize summaries, patches, timestamps, statuses, or ownership.

**Acceptance:**

- Preserved hashes match before recovery; mismatches fail closed.
- Projection tests PASS for every vocabulary category, unknown fallback, stable grouping, late insertion, duplicate/conflicting IDs, critical-row preservation, and cap visibility.
- Collector/factory contract tests PASS for valid run/task/attempt/phase/path linkage and negative missing/conflicting cases; wrong diff is never attached.
- DiffViewer tests PASS for split/unified, selected file, multi-file, add/delete/modify/rename, context collapse, empty, binary, mode-only, submodule, rename-only, quoted path, missing newline, malformed hunk, and truncation warnings.
- Interaction tests PASS for right-click, `Shift+F10`, Context Menu key, visible diagnostics parity, focus movement/restoration, Escape, `aria-expanded`/`aria-controls`, no focus theft, and restrained live announcements.
- Diagnostics tests PASS for redaction, escaping, HTML/ANSI inertness, 24 KiB bounds, and truncation labels.
- Gallery covers both themes, long paths, empty/missing/truncated/malformed states, all file statuses, and narrow/wide layouts.
- Run mandatory current project testing doctrine before tests. Required gates: `pnpm --filter @overdeck/deck-ui test`, `pnpm --filter @overdeck/deck-ui typecheck`, `pnpm --filter web build`, `pnpm --filter web typecheck`, affected collector tests, and factory suite when harness machinery changes. All PASS with no ignored warnings.
- Browser proof uses installed app and real recorded agent data: clicked agent opens story-first view; routine group expands; phase evidence is labeled honestly; exact linked edit expands correct side-by-side patch; context menu and visible button open same diagnostics; raw details stay hidden until requested; dark/light and wide/narrow have no body overflow.
- Land and deploy through canonical project workflow. Done only when owner can use installed result.

**Preserved WIP:**

- Worktree: `/home/user/Projects/overdeck/.worktrees/agent-observability-design`, branch `wt/agent-observability-design`.
- Submitted evidence remains untouched under `/home/user/Projects/overdeck/temp-user/project-onboarding/`.
- Shared checkout WIP remains untouched.

**Current receipt:** DONE. Production linkage landed on `origin/main` in `9b84c1b03`; installed Overdeck advanced through `dfd3339f0`, which contains that feature revision. Full authoritative browser proof passed from real project/run/timeline/agent navigation: routine group expansion, inline linked split diff, honest `Phase changes` label, header diagnostics, visible row diagnostics, right-click diagnostics, test/warning/outcome rows, hidden branch/token/attempt identity, and no body overflow all asserted true. Refreshed `clicked-agent-story.png` and `clicked-agent-diff.png` were produced. Browser-proof portability fix `cd8b40eab` also landed on `origin/main`. Factory full suite passed 189 tests and web typecheck passed with zero errors, warnings, or hints; earlier pre-merge receipts passed collector, focused web, web build, and browser flow.

**Acceptance delta:** Post-merge collector, focused web, and web-build reruns were interrupted by separate buildbox managed-runtime migration, never counted as PASS. The linkage lander's own deploy request timed out waiting for the deploy lock, but another queued deploy installed descendant `dfd3339f0`; installed-revision inspection therefore proves the feature reached the live checkout. The managed-runtime migration remains separate infrastructure work and does not reopen this completed feature unless installed behavior regresses.

**Next executable action:** None. Reopen only for a reproduced installed-flow regression.

## 2026-08-16 rehoming receipt

The earlier DONE receipt was not owner-reachable: the activity story was mounted only below the retired `/plans` surface, so the owner could not navigate to it from the live product.

This recovery rehomes the same `AgentApp`, `AgentFeed`, diagnostics drawer, and `DiffViewer` onto `/factory/[adwId]/agents/[agentId]`. Factory attempt rows expose `Open activity` only when the trace carries authoritative task identity; legacy rows without that identity remain unlinked rather than guessing. The request drawer exposes `Open activity` only when its recorded proof points at a factory run; CLI session attribution alone is not treated as a factory identity. The retired deep route remains untouched and no `/plans` navigation was restored.

The eight observability-design commits reviewed from the preserved lineage (`c8ac3c6e`, `fa80351f`, `f292df3d`, `0610e23e`, `f73a9fb3`, `88d0527f`, `ebe9eff9`, `cd8b40ea`) were already ancestors of this lane. No duplicate cherry-picks were applied and none were left behind for incompatibility.
