feat(face): integrate Idle Task Protocol v1.0 end-to-end
Watchface now syncs with the Nine Hazes idle task system via its own
pkjs client — the three-artifact architecture is operational.
Wire side:
- pkjs fetches GET /v1/queue on launch, applies clock offset, converts
epoch-ms to seconds for AppMessage, resolves task name from the
cached dictionary (refetch on version drift)
- GameSnapshot persisted at key 0x30 (protocol-specified); task name
at 0x32 (face-local nicety)
- AppMessage inbox widened to 256 bytes to carry the full snapshot
dictionary in a single logical message
Display side:
- Task strip renders two lines: countdown-to-completion + used/36 +
task count, then task name with "(+)" pending-rewards cue
- Status states render distinctly: FROZEN / ABORTED(code) / LOCKED /
No hero yet (EMPTY)
- serverDown sentinel key: oxblood "!" icon on failed check; absence
on successful sync clears it; volatile — never persisted
- Countdown math is server-clock corrected (clock_off), computed
locally from bound_sec — zero radio to stay fresh
Also:
- ES5-normalized pkjs for SDK webpack 1.15/acorn parser
- Replaced face_packet.{h,c} with snapshot.{h,c} (PACKET_KEY 101
retired; old blob orphaned harmlessly)
- Mock Task API server seeded with 3-task queue (in-memory)
Verified: seeded queue renders "0:xx 16/36 seg 3 tasks / Gather (+)";
server kill → cached strip + offline icon, no error surface.
This commit is contained in:
89
docs/PROTOCOL.md
Normal file
89
docs/PROTOCOL.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user