Files
Timmy-time-dashboard/docs/adr/022-real-time-activity-feed.md
Alexander Payne d8d976aa60 feat: complete Event Log, Ledger, Memory, Cascade Router, Upgrade Queue, Activity Feed
This commit implements six major features:

1. Event Log System (src/swarm/event_log.py)
   - SQLite-based audit trail for all swarm events
   - Task lifecycle tracking (created, assigned, completed, failed)
   - Agent lifecycle tracking (joined, left, status changes)
   - Integrated with coordinator for automatic logging
   - Dashboard page at /swarm/events

2. Lightning Ledger (src/lightning/ledger.py)
   - Transaction tracking for Lightning Network payments
   - Balance calculations (incoming, outgoing, net, available)
   - Integrated with payment_handler for automatic logging
   - Dashboard page at /lightning/ledger

3. Semantic Memory / Vector Store (src/memory/vector_store.py)
   - Embedding-based similarity search for Echo agent
   - Fallback to keyword matching if sentence-transformers unavailable
   - Personal facts storage and retrieval
   - Dashboard page at /memory

4. Cascade Router Integration (src/timmy/cascade_adapter.py)
   - Automatic LLM failover between providers (Ollama → AirLLM → API)
   - Circuit breaker pattern for failing providers
   - Metrics tracking per provider (latency, error rates)
   - Dashboard status page at /router/status

5. Self-Upgrade Approval Queue (src/upgrades/)
   - State machine for self-modifications: proposed → approved/rejected → applied/failed
   - Human approval required before applying changes
   - Git integration for branch management
   - Dashboard queue at /self-modify/queue

6. Real-Time Activity Feed (src/events/broadcaster.py)
   - WebSocket-based live activity streaming
   - Bridges event_log to dashboard clients
   - Activity panel on /swarm/live

Tests:
- 101 unit tests passing
- 4 new E2E test files for Selenium testing
- Run with: SELENIUM_UI=1 pytest tests/functional/ -v --headed

Documentation:
- 6 ADRs (017-022) documenting architecture decisions
- Implementation summary in docs/IMPLEMENTATION_SUMMARY.md
- Architecture diagram in docs/architecture-v2.md
2026-02-26 08:01:01 -05:00

6.2 KiB

ADR 022: Real-Time Activity Feed

Status

Proposed

Context

The dashboard currently shows static snapshots of swarm state. Users must refresh to see:

  • New tasks being created
  • Agents joining/leaving
  • Bids being submitted
  • Tasks being completed

This creates a poor UX for monitoring the swarm in real-time.

Decision

Implement a WebSocket-based real-time activity feed that streams events from the Event Log to connected dashboard clients.

Architecture

Data Flow

Coordinator Event → Event Log (SQLite)
       ↓
WebSocket Broadcast
       ↓
Dashboard Clients (via ws_manager)

Components

  1. Event Source (src/swarm/coordinator.py)

    • Already emits events via log_event()
    • Events are persisted to SQLite
  2. WebSocket Bridge (src/ws_manager/handler.py)

    • Already exists for agent status
    • Extend to broadcast events
  3. Event Broadcaster (src/events/broadcaster.py - NEW)

    class EventBroadcaster:
        """Bridges event_log → WebSocket."""
    
        async def on_event_logged(self, event: EventLogEntry):
            """Called when new event is logged."""
            await ws_manager.broadcast_event({
                "type": event.event_type.value,
                "source": event.source,
                "task_id": event.task_id,
                "agent_id": event.agent_id,
                "timestamp": event.timestamp,
                "data": event.data,
            })
    
  4. Dashboard UI (/swarm/live - enhanced)

    • Already exists at /swarm/live
    • Add activity feed panel
    • Connect to WebSocket
    • Show real-time events
  5. Mobile Support

    • Same WebSocket for mobile view
    • Simplified activity list

Event Types to Broadcast

Event Type Display As Icon
task.created "New task: {description}" 📝
task.assigned "Task assigned to {agent}" 👤
task.completed "Task completed"
agent.joined "Agent {name} joined" 🟢
agent.left "Agent {name} left" 🔴
bid.submitted "Bid: {amount}sats from {agent}" 💰
tool.called "Tool: {tool_name}" 🔧
system.error "Error: {message}" ⚠️

WebSocket Protocol

// Client connects
{"action": "subscribe", "channel": "events"}

// Server broadcasts
{
  "type": "event",
  "payload": {
    "event_type": "task.assigned",
    "source": "coordinator",
    "task_id": "task-123",
    "agent_id": "agent-456",
    "timestamp": "2024-01-15T10:30:00Z",
    "data": {"bid_sats": 100}
  }
}

UI Design: Activity Feed Panel

┌─────────────────────────────────────────┐
│ LIVE ACTIVITY                    [🔴]  │
├─────────────────────────────────────────┤
│ 📝 New task: Write Python function      │
│    10:30:01                             │
│ 💰 Bid: 50sats from forge               │
│    10:30:02                             │
│ 👤 Task assigned to forge               │
│    10:30:07                             │
│ ✓ Task completed                        │
│    10:30:15                             │
│ 🟢 Agent Echo joined                    │
│    10:31:00                             │
│                                         │
│ [Show All Events]                       │
└─────────────────────────────────────────┘

Integration with Existing Systems

Existing: Event Log (src/swarm/event_log.py)

  • Hook into log_event() to trigger broadcasts
  • Use SQLite AFTER INSERT trigger or Python callback

Existing: WebSocket Manager (src/ws_manager/handler.py)

  • Add broadcast_event() method
  • Handle client subscriptions

Existing: Coordinator (src/swarm/coordinator.py)

  • Already calls log_event() for all lifecycle events
  • No changes needed

Existing: Swarm Live Page (/swarm/live)

  • Enhance with activity feed panel
  • WebSocket client connection

Technical Design

Option A: Direct Callback (Chosen)

Modify log_event() to call broadcaster directly.

Pros: Simple, immediate delivery Cons: Tight coupling

# In event_log.py
def log_event(...):
    # ... store in DB ...
    
    # Broadcast to WebSocket clients
    asyncio.create_task(_broadcast_event(event))

Option B: SQLite Trigger + Poll

Use SQLite trigger to mark new events, poll from broadcaster.

Pros: Decoupled, survives restarts Cons: Latency from polling

Option C: Event Bus

Use existing src/events/bus.py to publish/subscribe.

Pros: Decoupled, flexible Cons: Additional complexity

Decision: Option A for simplicity, with Option C as future refactoring.

Performance Considerations

  • Rate Limiting: Max 10 events/second to clients
  • Buffering: If client disconnected, buffer last 100 events
  • Filtering: Clients can filter by event type
  • Deduplication: WebSocket manager handles client dedup

Security

  • Only authenticated dashboard users receive events
  • Sanitize event data (no secrets in logs)
  • Rate limit connections per IP

Consequences

Positive

  • Real-time visibility into swarm activity
  • Better UX for monitoring
  • Uses existing infrastructure (Event Log, WebSocket)

Negative

  • Increased server load from WebSocket connections
  • Event data must be carefully sanitized
  • More complex client-side state management

Mitigations

  • Event throttling
  • Connection limits
  • Graceful degradation to polling

Implementation Plan

  1. Create EventBroadcaster - Bridge event_log → ws_manager
  2. Extend ws_manager - Add broadcast_event() method
  3. Modify event_log.py - Hook in broadcaster
  4. Enhance /swarm/live - Add activity feed panel with WebSocket
  5. Create EventFeed component - Reusable HTMX + WebSocket widget
  6. Write tests - E2E tests for real-time updates

Dependencies

  • Existing src/swarm/event_log.py
  • Existing src/ws_manager/handler.py
  • Existing /swarm/live page
  • HTMX WebSocket extension (already loaded)