forked from Rockachopa/Timmy-time-dashboard
5.2 KiB
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.jsonas 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").
Related
- #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)