Problem
The TUI polls the daemon every 2s for loop status changes. This means up to 2s latency for status updates, wasted work when nothing changes, and no real-time feel. The daemon already knows when state changes - it just doesn't tell connected clients.
Solution
Add a push/subscribe mechanism: daemon pushes state changes to connected clients in real-time. TUI subscribes instead of polling.
Architecture
Current:
TUI -> every 2s -> GET /list -> daemon returns all loops
Proposed:
TUI -> subscribe once -> daemon pushes diffs on state change
TUI -> every 10s -> GET /list (fallback sync, handles missed pushes)
IPC protocol extension
New request type: subscribe
{"type": "subscribe"}
Daemon tracks subscribed sockets. On any state change (loop started, paused, completed run, etc.), daemon pushes:
{"type": "event", "event": "loop-updated", "data": {...loopMeta}}
Event types:
- loop-updated (status, runCount, lastExitCode changed)
- loop-created
- loop-deleted
- config-reloaded (from hot-reload, issue #10)
Benefits
- Real-time status updates (no 2s delay)
- Less work when idle (no polling)
- Hot-reload changes pushed to TUI instantly
- Feels like a live dashboard, not a polling app
Backward compatibility
- Subscribe is optional - old clients still work with polling
- TUI keeps a 10s fallback poll as safety net
- No breaking changes to existing IPC protocol
Acceptance criteria
Part of v2.0.0 epic #4
Problem
The TUI polls the daemon every 2s for loop status changes. This means up to 2s latency for status updates, wasted work when nothing changes, and no real-time feel. The daemon already knows when state changes - it just doesn't tell connected clients.
Solution
Add a push/subscribe mechanism: daemon pushes state changes to connected clients in real-time. TUI subscribes instead of polling.
Architecture
Current:
TUI -> every 2s -> GET /list -> daemon returns all loops
Proposed:
TUI -> subscribe once -> daemon pushes diffs on state change
TUI -> every 10s -> GET /list (fallback sync, handles missed pushes)
IPC protocol extension
New request type:
subscribe{"type": "subscribe"}
Daemon tracks subscribed sockets. On any state change (loop started, paused, completed run, etc.), daemon pushes:
{"type": "event", "event": "loop-updated", "data": {...loopMeta}}
Event types:
- loop-updated (status, runCount, lastExitCode changed)
- loop-created
- loop-deleted
- config-reloaded (from hot-reload, issue #10)
Benefits
Backward compatibility
Acceptance criteria
Part of v2.0.0 epic #4