# Nine Hazes — Watch Client for OSRIC MUD Idle System ## Consolidated record v3.0 (post-pivot, 2026-09-12) ### 1. Concept Pebble Time 2 (emery) watchface + companion watchapp acting as remote monitor and manager for the Nine Hazes OSRIC MUD idle task system. NOT a standalone game — all game logic lives on the MUD server. The watch displays server snapshots and submits intents (add/cancel/claim); the server validates and executes. Watch behaves like a normal watchface except during active sync. ### 2. Critical architecture decision — "face gets its own sync" Pebble persist storage is per-app sandboxed with NO app↔app communication (verified against platform docs). Therefore the watchface and watchapp each own an independent pkjs sync to the same Task API. No watch-side data sharing exists or is attempted. Shared truth = server. ### 3. Protocol Wire contract: "Nine Hazes — Idle Task Protocol v1.0" (separate document, authored server-side; authoritative). Summary: - HTTPS + Bearer token (scoped queue:read queue:write claim; token on phone localStorage only, NEVER on watch) - GET /v1/dictionary (versioned, 304-style caching via ?v=) - GET /v1/queue — full sync in one call (tasks[], used/budget, pend, bound, status, tok.exp) - POST /v1/queue/tasks (reqId idempotency), DELETE /v1/queue/tasks/:tid, POST /v1/claim, POST /v1/token/refresh - Device protocol: GameSnapshot packed struct (~28 bytes) at persist key 0x30; dictionary blob at 0x31 (watchapp use) - Clock correction: all countdowns from bound + clock_off, never raw watch clock - Failure ladder: unreachable/malformed → silent, render cached snapshot ### 4. Artifacts & ownership | Artifact | Owner | Status | |---|---|---| | Watchface (C, canvas renderer, widget table) | watch team | RENDERING BASELINE COMPLETE: custom-canvas data-driven renderer, 10 widget types + 2 spare slots, per-widget layout persist (key 100), parchment/ink palette, task strip rendering, server-down icon, dev.sh tooling. Committed to Gitea. | | Watchapp (queue manager UI) | watch team | deferred (build order item) | | pkjs ×2 (face sync, watchapp sync) | watch team | face pkjs fetches /v1/queue → AppMessage snapshot | | Bridge/state server | OBSOLETE — deleted. Superseded by MUD gateway + placeholder mock (below) | | Placeholder mock server (idle protocol §10) | watch team | zero-dependency node:http implementation (NOT Express — locked project law: TypeScript, node stdlib only) | ### 5. Widget set (unchanged) Time, date, watch battery (+days estimator, key 102), phone battery, steps, distance, weather composite (NWS default / Open-Meteo fallback — geographic auto-switch), advisory omens, sleep, Current Task Strip (2-line: line 1 = countdown to task completion + used/36 + task count, line 2 = current task name (+n) pending indicator; offline "!" icon on failed check). All toggleable/movable/font+color stylable. Spare slots reserved (incl. game preview / companion preview, deferred). ### 6. Design laws (north stars) 1. Casual glance — no game-triggered interrupts, ever. 2. Game logic dormant until invited; watch computes only countdown strings. 3. Fully free face — no protected widgets; "restore default" escape hatch. 4. Face shows minimal: countdown + queue line only. 5. Battery-first: zero network in ambient use; one sync per interaction. 6. Fail-dead-silent: server down → cached data or hidden strip; no errors. 7. Zero-dependency TypeScript server code; no workers/background radio ever; wakeups = semantic events only, rolling single wakeup (Muninn pattern). ### 7. Platform/environment emery only ("targetPlatforms": ["emery"]) · Core Devices PebbleOS SDK · system fonts first (carved from Apache release), OFL TTFs bundled later · Apache-2.0 · Gitea master (auth-gated), GitHub public mirror at v0.1.0 · Liberapay/Ko-fi FUNDING.yml. ### 8. Reference projects (ideas only, clean-room; unlicensed repos = read-for-protocol never copy code; Remix-on-CloudPebble ≠ redistribution grant) IdleFantasy (queue model), Doors of Doom (AppMessage), Pixel8 (chunked transfer), Muninn (wakeup/battery sampling patterns), Tamagotchi-API/TamaTime (companion-preview idea, DEFERRED — feature not focus). ### 9. Build order (current) 1. ✔ Watchface rendering baseline (committed) 2. ✔ pkjs fetch + AppMessage + cached-packet strip + offline icon (v1 protocol) 3. → NOW: idle-protocol adaptation — snapshot struct, mock server, clock-offset countdown (this revision) 4. Widget wiring: health (steps/distance/sleep), weather (NWS/Open-Meteo) 5. Watchapp: queue UI (add via presets/segment picker, cancel, claim), wakeup at segment boundary, launch_args deep links, App Glance/timeline pins 6. Settings phase 2: hosted config page (drag-drop layout, presets, server URL/ token entry) 7. Polish: PDC sprites/icons (pickaxe, fist, coin, moon), theme, vibration ### 10. Open items to relay to server team - bound/t/sync timestamps in epoch-ms exceed AppMessage uint32 → watchface uses epoch-SECONDS for wire transfer (recommend protocol clarify units) - Face receives resolved task NAME from pkjs (v1); dictionary blob 0x31 is watchapp-scoped, chunked transfer deferred - Player identity: X-Pebble-Id informational; real auth = MUD token