# Event Log

Every structural change appends an immutable row to `structure_events`. The table is **append-only** — rows are never updated or deleted. This is the event sourcing pattern.

## How It Works

The `workspaces` table is a **projection** of these events — it represents current state. If you replay all events from the beginning, you get the same `workspaces` table.

## Event Types

**The rule: structural changes are persisted, presentational changes are ephemeral.**

The test: "If I replayed all `structure_events` from scratch, would I reconstruct a fully working system?" If yes, the event log is complete. Canvas positions don't affect system behavior — they belong in `canvas_layouts`, not `structure_events`.

### Persisted to structure_events (AND broadcast via WebSocket)

```
WORKSPACE_PROVISIONING       workspace being created
WORKSPACE_PROVISION_FAILED   launch error — needs audit trail
WORKSPACE_ONLINE             availability change
WORKSPACE_OFFLINE            availability change
WORKSPACE_DEGRADED           health change
WORKSPACE_REMOVED            permanent change
WORKSPACE_MOVED              hierarchy change (different parent, NOT canvas drag)
WORKSPACE_EXPANDED           hierarchy change
WORKSPACE_COLLAPSED          hierarchy change
AGENT_ASSIGNED               agent change
AGENT_REMOVED                agent change
AGENT_REPLACED               agent change
AGENT_MOVED                  agent moved between workspaces
AGENT_CARD_UPDATED           capabilities changed
```

### WebSocket only, NOT persisted to structure_events

```
TASK_UPDATED                 current task changed — saved to workspaces.current_task
ACTIVITY_LOGGED              activity log created — saved to activity_logs table
Canvas node dragged          presentational — saved to canvas_layouts
Canvas viewport changed      presentational — saved to canvas_viewport
Node collapsed/expanded      presentational UI toggle — saved to canvas_layouts
  (different from WORKSPACE_EXPANDED which is a structural team expansion)
```

`TASK_UPDATED` and `ACTIVITY_LOGGED` use `BroadcastOnly()` — they are sent over WebSocket but not recorded in `structure_events`. They have their own storage: `workspaces.current_task` column and `activity_logs` table respectively. This separation keeps `structure_events` focused on structural changes that define the org chart, while operational activity (A2A communications, task updates, agent logs) lives in `activity_logs` with its own retention policy.

`WORKSPACE_MOVED` is the nuanced case: a workspace moved to a different parent (hierarchy change) is structural and persisted. A node dragged to a new canvas position is presentational and saved to `canvas_layouts` only.

See [WebSocket Events](../api-protocol/websocket-events.md) for the full JSON payload of each event type.

## Why This Matters

1. **Complete audit trail** — you always know exactly what happened and when
2. **Meaningful responses for removed workspaces** — other workspaces can get context when querying a workspace that no longer exists
3. **Canvas timeline** — the canvas can show a timeline of changes for any workspace
4. **Easier debugging** — debugging distributed agent systems becomes much easier with a full history
5. **Future: time travel** — replay to any point in time ("what did our org chart look like last Tuesday?")

## Schema

```sql
CREATE TABLE structure_events (
  id            UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  event_type    TEXT NOT NULL,
  workspace_id  UUID,
  agent_id      UUID,
  target_id     UUID,
  payload       JSONB,
  created_at    TIMESTAMPTZ DEFAULT now()
);
```

The `payload` JSONB field contains event-specific data (e.g. the old and new URL for a workspace move, the reason for a deletion).

## Rules

- **Never UPDATE or DELETE rows** in `structure_events`. It is append-only.
- The `workspaces` table is the mutable projection — update that instead.
- Every state change must produce an event before updating the projection.

## Related Docs

- [Database Schema](./database-schema.md) — Full table definitions
- [Registry & Heartbeat](../api-protocol/registry-and-heartbeat.md) — Events generated by registration
- [Platform API](../api-protocol/platform-api.md) — Event query endpoints
- [WebSocket Events](../api-protocol/websocket-events.md) — Full event payload reference
