diff --git a/content/docs/architecture/event-log.md b/content/docs/architecture/event-log.md new file mode 100644 index 0000000..3fdd0a3 --- /dev/null +++ b/content/docs/architecture/event-log.md @@ -0,0 +1,86 @@ +# 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