--- id: TASK-43 title: 'Implement Event Broadcasting: canvas-wide pub/sub system' status: To Do assignee: [] created_date: '2026-02-18 20:06' labels: - feature - phase-2 - ecosystem milestone: m-1 dependencies: - TASK-41 references: - rspace-online/lib/community-sync.ts - rspace-online/server/community-store.ts priority: medium --- ## Description Add a pub/sub event system so shapes can broadcast and subscribe to named events across the canvas. New file lib/event-bus.ts: - CanvasEventBus class with emit(), subscribe(), unsubscribe(), getSubscribers() - Events written to CRDT doc.eventLog (bounded ring buffer, last 100 entries) - Remote users see events replayed via Automerge patch application - Re-entrancy guard kills chains after 10 levels to prevent infinite loops Automerge schema additions: - doc.eventLog: EventEntry[] (id, channel, sourceShapeId, payload, timestamp) - shapes[id].subscriptions: string[] (channel names) Shapes opt in with onEventReceived(channel, payload) method. Example: Timer emits "timer:done" → all subscribed Budget shapes recalculate. ## Acceptance Criteria - [ ] #1 CanvasEventBus emits events to CRDT eventLog - [ ] #2 Shapes can subscribe to channels and receive events - [ ] #3 Events sync to remote users via Automerge - [ ] #4 Ring buffer bounded at 100 entries with GC - [ ] #5 Re-entrancy guard prevents infinite event loops - [ ] #6 Works offline (events queued in CRDT, replayed on reconnect)