# The Interaction Contract

Each component writes the same event to the host through `onEvent`. Keep them in one log per run; the Review Receipt reads that log.

```ts
interface InteractionEvent<P = unknown> {
  id: string;             // unique per event
  runId: string;          // one agent run
  actor: 'human' | 'agent' | 'system';
  type: InteractionEventType;
  at: string;             // ISO timestamp
  correlationId?: string; // ties an approval to the plan, a step to the plan step
  payload: P;
}
```

Import the types and helpers from `@/lib/interaction-contract`: `createEvent(runId, draft)`, `isInteractionEvent(x)` for events read back from storage, `isEventOf(e, type)`.

## The lifecycle of one run

```text
plan.proposed → plan.accepted → approval.requested → approval.granted
→ execution.started → execution.progress (each step change) → execution.completed
→ review.recorded (kind: 'run')
```

Other types: `plan.revised`, `approval.denied`, `approval.expired`, `approval.revoked`, `execution.retried`, `execution.failed`, `execution.deviated`, `execution.paused`, `execution.resumed`, `execution.stopped`. The host may emit the last four itself with `createEvent`.

## Who emits what

| Component | Events | correlationId |
|---|---|---|
| Plan Preview | `plan.proposed`, `plan.revised`, `plan.accepted` (payload: the frozen `PlanSnapshot`) | the plan id |
| Approval Gate | `approval.requested` per version, then `granted`, `denied`, `expired` or `revoked` | the `correlationId` prop: pass the plan id |
| Progress Trace | `execution.started`, `execution.retried`, `execution.progress` per step change, `execution.completed`, `execution.failed` | the step's `planStepId` |
| Review Receipt | `review.recorded` with `kind: 'run'`, the verdict, and the plan, request and receipt ids | the plan id |

## The shared records

- `PlanSnapshot { planId, version, steps }`: what the person accepted. An edit after acceptance makes a new version that needs its own accept.
- `ActionRequest { id, version, action, args, resources, consequences?, reversible }`: what the agent asks to do. The server builds and stores it. A change is a new `version`.
- `ApprovalDecision { requestId, requestVersion, outcome, scope, editedArgs?, decidedAt, serverReceiptId? }`: one decision on one version. `serverReceiptId` is set only by the server after it verifies.

## Rules that keep the log useful

- One `runId` for the whole run, on each component.
- The approval's `correlationId` is the plan id. The receipt names an approval that points elsewhere.
- Each trace step carries the `planStepId` it runs. The receipt names a step that ran outside the plan.
- Store the log on the server. The browser copy is for the screen.
