1
0
This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/docs/adr/023-workshop-presence-schema.md

5.2 KiB

ADR-023: Workshop Presence Schema

Status: Accepted Date: 2026-03-18 Issue: #265 Epic: #222 (The Workshop)

Context

The Workshop renders Timmy as a living presence in a 3D world. It needs to know what Timmy is doing right now — his working memory, not his full identity or history. This schema defines the contract between Timmy (writer) and the Workshop (reader).

Design principles:

  • Working memory, not long-term memory. Present tense only.
  • Written as side effect of work. Not a separate obligation.
  • Liveness is mandatory. Stale = "not home," shown honestly.
  • Schema is the contract. Keep it minimal and stable.

Decision

File Location

~/.timmy/presence.json

JSON chosen over YAML for predictable parsing by both Python and JavaScript (the Workshop frontend). The Workshop reads this file via the WebSocket bridge (#243) or polls it directly during development.

Schema (v1)

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Timmy Presence State",
  "description": "Working memory surface for the Workshop renderer",
  "type": "object",
  "required": ["version", "liveness", "current_focus"],
  "properties": {
    "version": {
      "type": "integer",
      "const": 1,
      "description": "Schema version for forward compatibility"
    },
    "liveness": {
      "type": "string",
      "format": "date-time",
      "description": "ISO 8601 timestamp of last update. If stale (>5min), Timmy is not home."
    },
    "current_focus": {
      "type": "string",
      "description": "One sentence: what Timmy is doing right now. Empty string = idle."
    },
    "active_threads": {
      "type": "array",
      "maxItems": 10,
      "description": "Current work items Timmy is tracking",
      "items": {
        "type": "object",
        "required": ["type", "ref", "status"],
        "properties": {
          "type": {
            "type": "string",
            "enum": ["pr_review", "issue", "conversation", "research", "thinking"]
          },
          "ref": {
            "type": "string",
            "description": "Reference identifier (issue #, PR #, topic name)"
          },
          "status": {
            "type": "string",
            "enum": ["active", "idle", "blocked", "completed"]
          }
        }
      }
    },
    "recent_events": {
      "type": "array",
      "maxItems": 20,
      "description": "Recent events, newest first. Capped at 20.",
      "items": {
        "type": "object",
        "required": ["timestamp", "event"],
        "properties": {
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "event": {
            "type": "string",
            "description": "Brief description of what happened"
          }
        }
      }
    },
    "concerns": {
      "type": "array",
      "maxItems": 5,
      "description": "Things Timmy is uncertain or worried about. Flat list, no severity.",
      "items": {
        "type": "string"
      }
    },
    "mood": {
      "type": "string",
      "enum": ["focused", "exploring", "uncertain", "excited", "tired", "idle"],
      "description": "Emotional texture for the Workshop to render. Optional."
    }
  }
}

Example

{
  "version": 1,
  "liveness": "2026-03-18T21:47:12Z",
  "current_focus": "Reviewing PR #267 — stream adapter for Gitea webhooks",
  "active_threads": [
    {"type": "pr_review", "ref": "#267", "status": "active"},
    {"type": "issue", "ref": "#239", "status": "idle"},
    {"type": "conversation", "ref": "hermes-consultation", "status": "idle"}
  ],
  "recent_events": [
    {"timestamp": "2026-03-18T21:45:00Z", "event": "Completed PR review for #265"},
    {"timestamp": "2026-03-18T21:30:00Z", "event": "Filed issue #268 — flaky test in sensory loop"}
  ],
  "concerns": [
    "WebSocket reconnection logic feels brittle",
    "Not sure the barks system handles uncertainty well yet"
  ],
  "mood": "focused"
}

Design Answers

Question Answer
File format JSON (predictable for JS + Python, no YAML parser needed in browser)
recent_events cap 20 entries max, oldest dropped
concerns severity Flat list, no priority. Keep it simple.
File location ~/.timmy/presence.json — accessible to Workshop via bridge
Staleness threshold 5 minutes without liveness update = "not home"
mood field Optional. Workshop can render visual cues (color, animation)

Consequences

  • Timmy's agent loop must write ~/.timmy/presence.json as a side effect of work. This is a hook at the end of each cycle, not a daemon.
  • The Workshop frontend reads this file and renders accordingly. Stale liveness → dim the wizard, show "away" state.
  • The WebSocket bridge (#243) watches this file and pushes changes to connected Workshop clients.
  • Schema is versioned. Breaking changes increment the version field. Workshop must handle unknown versions gracefully (show raw data or "unknown state").
  • #222 — Workshop epic
  • #243 — WebSocket bridge (transports this state)
  • #239 — Sensory loop (feeds into state)
  • #242 — 3D world (consumes this state for rendering)
  • #246 — Confidence as visible trait (mood field serves this)