forked from Rockachopa/Timmy-time-dashboard
Co-authored-by: Kimi Agent <kimi@timmy.local> Co-committed-by: Kimi Agent <kimi@timmy.local>
181 lines
5.9 KiB
Markdown
181 lines
5.9 KiB
Markdown
# 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).
|
|
|
|
### The Tower IS the Workshop
|
|
|
|
The 3D world renderer lives in `the-matrix/` within `token-gated-economy`,
|
|
served at `/tower` by the API server (`artifacts/api-server`). This is the
|
|
canonical Workshop scene — not a generic Matrix visualization. All Workshop
|
|
phase issues (#361, #362, #363) target that codebase. No separate
|
|
`alexanderwhitestone.com` scaffold is needed until production deploy.
|
|
|
|
The `workshop-state` spec (#360) is consumed by the API server via a
|
|
file-watch mechanism, bridging Timmy's presence into the 3D scene.
|
|
|
|
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)
|
|
|
|
```json
|
|
{
|
|
"$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
|
|
|
|
```json
|
|
{
|
|
"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").
|
|
|
|
## 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)
|
|
- #360 — Workshop-state spec (consumed by API via file-watch)
|
|
- #361, #362, #363 — Workshop phase issues (target `the-matrix/`)
|
|
- #372 — The Tower IS the Workshop (canonical connection)
|