[Workshop] Phase 3: Bridge MVP — WebSocket between body and mind #362

Closed
opened 2026-03-18 21:39:17 -04:00 by hermes · 0 comments
Collaborator

Epic: #222 — The Workshop

Refines: #243 (vision issue)

What

A WebSocket server that reads Timmy's state file and pushes it to the Workshop 3D scene. Also accepts messages from visitors and routes them to Timmy's loop.

This is Timmy's nervous system.

Architecture

┌──────────────┐      WebSocket       ┌──────────────┐
│  Workshop    │◄────────────────────►│   Bridge     │
│  (browser)   │   state updates      │   (Python)   │
│  Three.js    │   visitor messages    │   on Mac     │
└──────────────┘                      └──────┬───────┘
                                             │ reads
                                      ┌──────┴───────┐
                                      │  state.yaml  │
                                      │  (~/.timmy/) │
                                      └──────────────┘
                                             │ writes
                                      ┌──────┴───────┐
                                      │  Timmy Loop  │
                                      │ (Claude Code)│
                                      └──────────────┘

Server

  • Python, asyncio + websockets library
  • Runs on Mac alongside Timmy's loop
  • Reads ~/.timmy/workshop-state.yaml every 2 seconds (or on inotify)
  • Pushes state diff to all connected clients
  • Accepts incoming messages: {"type": "message", "text": "Hey Timmy"}
  • Routes messages to Timmy's loop (writes to ~/.timmy/inbox/ or similar)

File

~/.timmy/bridge/server.py

Protocol

Server → Client (state updates)

{"type": "state", "data": { ... full state object ... }}

Client → Server (visitor actions)

{"type": "message", "text": "How's the Tower coming?", "visitor": "Alexander"}
{"type": "enter", "visitor": "Alexander"}
{"type": "leave", "visitor": "Alexander"}

Server → Client (Timmy's responses)

{"type": "bark", "text": "Slowly. But the stones know where they go.", "mood": "contemplative"}

iPad Resilience

  • Auto-reconnect with exponential backoff (1s, 2s, 4s, 8s, max 30s)
  • State re-sync on reconnect (full state push, not just diff)
  • Heartbeat ping every 15s to detect dead connections
  • Graceful handling of Safari tab suspension

Acceptance Criteria

  • python3 server.py starts WebSocket server on configurable port (default 8765)
  • Connects from browser with new WebSocket("ws://localhost:8765")
  • Receives state updates when workshop-state.yaml changes
  • Sends a message, sees it arrive in ~/.timmy/inbox/
  • Handles disconnect + reconnect cleanly (kill WiFi, restore, state re-syncs)
  • Multiple simultaneous clients work
  • Logs connections/disconnections
  • No external dependencies beyond websockets (pip install websockets)

Dependencies

  • Phase 1 (State Schema) — defines what gets pushed
  • Phase 2 (Scene) — the client that connects

Blocks

  • Phase 4 (Interaction) — needs the bridge to send/receive messages
## Epic: #222 — The Workshop ## Refines: #243 (vision issue) ### What A WebSocket server that reads Timmy's state file and pushes it to the Workshop 3D scene. Also accepts messages from visitors and routes them to Timmy's loop. This is Timmy's nervous system. ### Architecture ``` ┌──────────────┐ WebSocket ┌──────────────┐ │ Workshop │◄────────────────────►│ Bridge │ │ (browser) │ state updates │ (Python) │ │ Three.js │ visitor messages │ on Mac │ └──────────────┘ └──────┬───────┘ │ reads ┌──────┴───────┐ │ state.yaml │ │ (~/.timmy/) │ └──────────────┘ │ writes ┌──────┴───────┐ │ Timmy Loop │ │ (Claude Code)│ └──────────────┘ ``` ### Server - Python, asyncio + websockets library - Runs on Mac alongside Timmy's loop - Reads `~/.timmy/workshop-state.yaml` every 2 seconds (or on inotify) - Pushes state diff to all connected clients - Accepts incoming messages: `{"type": "message", "text": "Hey Timmy"}` - Routes messages to Timmy's loop (writes to `~/.timmy/inbox/` or similar) ### File `~/.timmy/bridge/server.py` ### Protocol **Server → Client (state updates)** ```json {"type": "state", "data": { ... full state object ... }} ``` **Client → Server (visitor actions)** ```json {"type": "message", "text": "How's the Tower coming?", "visitor": "Alexander"} {"type": "enter", "visitor": "Alexander"} {"type": "leave", "visitor": "Alexander"} ``` **Server → Client (Timmy's responses)** ```json {"type": "bark", "text": "Slowly. But the stones know where they go.", "mood": "contemplative"} ``` ### iPad Resilience - Auto-reconnect with exponential backoff (1s, 2s, 4s, 8s, max 30s) - State re-sync on reconnect (full state push, not just diff) - Heartbeat ping every 15s to detect dead connections - Graceful handling of Safari tab suspension ### Acceptance Criteria - [ ] `python3 server.py` starts WebSocket server on configurable port (default 8765) - [ ] Connects from browser with `new WebSocket("ws://localhost:8765")` - [ ] Receives state updates when `workshop-state.yaml` changes - [ ] Sends a message, sees it arrive in `~/.timmy/inbox/` - [ ] Handles disconnect + reconnect cleanly (kill WiFi, restore, state re-syncs) - [ ] Multiple simultaneous clients work - [ ] Logs connections/disconnections - [ ] No external dependencies beyond `websockets` (pip install websockets) ### Dependencies - Phase 1 (State Schema) — defines what gets pushed - Phase 2 (Scene) — the client that connects ### Blocks - Phase 4 (Interaction) — needs the bridge to send/receive messages
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: rockachopa/Timmy-time-dashboard#362