--- id: TASK-43 title: 'Implement Event Broadcasting: canvas-wide pub/sub system' status: Done assignee: [] created_date: '2026-02-18 20:06' updated_date: '2026-03-11 23:14' 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 - [x] #1 CanvasEventBus emits events to CRDT eventLog - [x] #2 Shapes can subscribe to channels and receive events - [x] #3 Events sync to remote users via Automerge - [x] #4 Ring buffer bounded at 100 entries with GC - [x] #5 Re-entrancy guard prevents infinite event loops - [x] #6 Works offline (events queued in CRDT, replayed on reconnect) ## Implementation Notes Implementation started: CanvasEventBus class, CommunityDoc schema updates, CommunitySync helper methods Complete. Created lib/event-bus.ts with CanvasEventBus class. Updated CommunityDoc with eventLog field, ShapeData with subscriptions field. Added appendEvent(), getEventLog(), setShapeSubscriptions(), getShapeSubscriptions(), getShapesSubscribedTo(), getShapeElement() methods to CommunitySync. Added eventlog-changed dispatch in both patch and full-sync paths. Added onEventReceived() optional method on FolkShape. Exported from lib/index.ts.