CP event ingest and activity view
Child of epic 7191 "Converge every runner on one agent contract: ACP-native". Settled inputs: PRD (task 7189), architecture (task 7190). Architecture boundary C6. This child carries the wave's only schema migration in phase 1 (the registry child waits on it).
## Original requirement (PRD, verbatim)
**R3** (CP half): one normalised event stream per run (turn-item vocabulary: assistant_message, reasoning, command_execution, file_change, approval_request, user_input_request, subagent, usage, error, session_end); usage accounting and rate-limit recovery with a reset time.
## Approach (architecture, verbatim)
**C6: CP events ingest + task-view activity + Efficiency.** `POST /api/agent/sessions/{sid}/events`, storage (an uploaded session member, no new table), additive migration `sessions.native_session_ref`, the task view live activity stream and download, and the Efficiency reader for `usage` items, including `agent_reported` cost flagged. *Seam:* §3.1.
CP leg (daemon → CP): `POST /api/agent/sessions/{sid}/events` (batch of §3.1 events, idempotent on `seq`) in `apps/api/app/routes/agent_sessions.py`. The browser reads the stream from the CP (`GET /api/tasks/{id}/sessions/{sid}/events?after=<seq>`, polled every 1 s while the view is open). Risk R7: event volume — batching, stored as a session member (S3/Athena path), not DB rows.
**One usage source per attempt.** The dispatch fact `runtime` is stored on the session row. The sweep reads usage from the event stream when `runtime=acp` and from the vendor parser when `runtime=legacy`, never both. A console hand-off starts a new session row for the new front, so each front's usage is counted once. Own a test that a hand-off session's Efficiency total equals the sum of its fronts.
Planner decision (closes the writer of `sessions.runtime` without a cross-child edge): the same additive migration adds `sessions.runtime` with server default `legacy`; this child sets it to `acp` when the first event batch for a session is ingested. The runtime-switch child does not write the column. Migrations must stay additive (coding standards; revision ID convention).
§3.1 event shape (implement the reader exactly): `{"v":1,"seq","ts","session_id","native_session_ref","task_id","provider","turn_id","item_id","kind","status","data"}`; kinds `session_start | turn_start | assistant_message | reasoning | command_execution | file_change | tool_call | approval_request | user_input_request | subagent | plan | usage | rate_limit | error | turn_end | session_end`; `usage.data` = `{model, input_tokens, cache_write_tokens, cache_read_tokens, output_tokens, reasoning_tokens, context_used, context_size, reported_cost_usd, source:"tokens"|"agent_reported"}`. Tokens are priced by `model_prices` exactly as today (`apps/api/app/model_prices.py:184`); `agent_reported` cost shown flagged in Efficiency with no price-table change.
Quality (tightened by the Planner): the activity stream shows tool names and file paths in full (wrapped, never truncated), approvals show what was approved and `decided_by`, errors show the vendor message verbatim; readable at desktop and phone widths; copy per `docs/standards/ui-copy-standards.md`.
Credentials & reach: AC-1–AC-4, AC-13–AC-18 | `jaah/debug/main/operator-login` (operator on dev), dev console `app.dev.jaah.ai` | verified. Gates | worktree only | needs nothing beyond the worktree.
Out of scope: Do NOT build the chat view or live controls (chat-view child), the node forwarder (host child), or a new events table. PRD §5 verbatim:
- MUST NOT delete the legacy paths (D5); MUST NOT file that task.
- MUST NOT publish (D2). MUST NOT change runner permission posture (D6). MUST NOT drop or regress any current Claude Code capability (R10). MUST NOT remove the console terminal/TUI view (D3). MUST NOT submit an upstream ACP RFD (D8). MUST NOT integrate with T3 Code's RPC or MCP API.
- MUST NOT change the task lifecycle states or the merge lane.
- MUST NOT change billing beyond ingesting ACP-reported usage into the existing usage path.
- MUST NOT add vendors. MUST NOT touch `apps/mobile`.
- MUST NOT put a token in the agent's environment or weaken process-ancestry identity (ADR-0083); MUST NOT add a second verb table.
Done means:
- AC-5 (R3) One event stream, one shape. → verify: for two different engines' completed runs, download the run's event stream from the task view / Audit > Sessions; observe identical schema (same item kinds and fields), and Efficiency shows usage for both without a vendor parser in the read path. (This child: API tests ingest two fixture streams with different providers; download returns both with identical schema; Efficiency totals equal the fixture token totals; ingest is idempotent on `seq`.)
- The hand-off test: a session with two fronts reports an Efficiency total equal to the sum of its fronts; `runtime=acp` sessions never invoke a vendor parser.
- AC-18 (stream half) Quality. → verify: visual review of the task-view activity stream on dev at desktop and phone widths: tool names and file paths shown in full, wrapped; approvals show what is approved; errors show the vendor message (Playwright screenshots at both widths attached to the PR).
Outcome
Built ACP event ingest (S3 chunk member, idempotent+gapless seq), sessions.runtime/native_session_ref migration, task-view Agent activity + download, Efficiency acp usage source. Quick gate green; lane merged #5925 (8368cd9); deploy superseded. Deferred: board agent-reported cost; draft #7311.
Artifacts
Activity
- Draft
- Ready
- Runner started
- Runner ended (done)