04 — Architecture Forge
**Track 4 of 4 in the leisure-goal synthesis.** **Subject:** Phone-as-cockpit, mesh-as-engine. The operating-system spine. **Anchor commit:** Pebble HEAD `3803b76` (V0.8 P5 Wave 4 iPad split-view shipped 2026-05-11).
Full Public Reader
04 — Architecture Forge
Track 4 of 4 in the leisure-goal synthesis.
Subject: Phone-as-cockpit, mesh-as-engine. The operating-system spine.
Anchor commit: Pebble HEAD `3803b76` (V0.8 P5 Wave 4 iPad split-view shipped 2026-05-11).
---
1. The Leisure Formula
Leisure(t) = (Captain ⊕ Workers ⊕ Sensors) ↦ Phone
│
governed by EW invariants
│
surfaced through
│
ControlOps · DataStreams · Notifications · Widgets · Voice · DeepLinks
│
↓
ergonomic minimum-friction(Mohamed)In plain prose:
> Leisure is the state where the home Macs hold the project memory, the home Macs do the work, and the iPhone is the cockpit. The phone never runs the system; the phone governs it. Confirmations are cheap, intervention is rare, and the system has a fallback for every degraded path so partial-mesh is the normal operating state, not the exception.
The formula has one binding constraint: every reachable mesh capability must be addressable from the phone, or it is a bug, not a phase-1 limitation.
---
2. The DNA Categories — 7 Archetypes of Work
These are the only legitimate shapes work can take in the leisure OS. Mixing two archetypes inside a single endpoint is forbidden (the Crucible architecture spine already enforces this for Pebble V0.8). Every new feature must declare its archetype before it gets built.
| # | Archetype | Plane | Failure mode | Concrete examples |
|---|---|---|---|---|
| 1 | ControlOp | Control | Throw loudly, never silently retry | Pebble send-prompt; aura-gateway `/inject`; codex-gateway `/codex/inject`; restart-service; switch-chat |
| 2 | DataStream | Data | Silent retry, banner only when stale | Pane snapshot polling; `/panes/<tty>/response`; mesh_pane_state reads; iMessage-like delivery dots |
| 3 | CaptainSummary | Bridge | Always falls back to gateway heuristic | `/captain/ask` `Where are we?`; rate-limit status; next-prompt drafts; `[CTRL]/[DATA]/[STATE]/[COMPACT]/[CONTEXT-RESET]` prefix language |
| 4 | SensorStream | Data | Drop frames on backpressure | KARL trajectories; cognitive-twin events; Femto/MediaPipe pose at LUMA :9703; ntfy idle-detection state machine on mac4 |
| 5 | AutopilotAgent | Bridge | Pause on first failure, never silent loop | Captain `draft N+1` → Codex auto-queue; Pulse sessions; ops:autopilot sleep-loop; mac4 idle-watcher → ntfy |
| 6 | WidgetGlance | Projection | Read-only, schema-checked, ≤120 char previews | Pebble Home Screen widget reading `<sharedContainer>/projection/latest.json`; MeshHealth tile (planned) |
| 7 | VoiceDictation | Control | Always editable before send | Pebble Whisper composer; cue-prompt drafts; future Siri Shortcuts via AppIntent |
Two cross-cutting modifiers attach to any archetype:
- DeepLinkJump — every conversation, widget tile, and notification surfaces a `pebble://` URI (`voice`, `chat/<id>`, `chat/<id>/voice`) so any glance can collapse into action in one tap.
- NotificationTrigger — ntfy push (`pebble-mohamed-94f7629a175c7e1d`) wakes the phone when a watcher transitions BUSY→IDLE; the body is a `CaptainSummary` when mac5 is live, a worker echo when it isn't.
---
3. Phone-to-Mesh Mapping (Control-Surface Table)
Each row is a phone affordance and the mesh capability it maps onto. If a row is missing, the leisure OS has a hole.
| Phone affordance | UI surface | Archetype | Mesh endpoint / actor | Fallback when degraded |
|---|---|---|---|---|
| Pull-to-refresh conversation list | Pebble sidebar | DataStream | `/panes?machine=<m>` discovery | Show cached last-known; banner |
| Tap conversation row | iPhone push / iPad detail | DeepLinkJump | NavigationSplitView selection | Empty-detail hexagon hero |
| Send text prompt | ChatView composer → send button | ControlOp | mac4 `/codex/inject` or mac1 `/inject` | 503 chat_unreachable banner → "send to active" |
| Hold mic → dictate | Voice composer (Whisper) | VoiceDictation | Local transcription → composer | Manual edit always available |
| Heart-icon "Where are we?" | Chat toolbar → WhereAreWeSheet | CaptainSummary | mac1 `/captain/ask` (gateway stub today, mac5 captain-bridge tomorrow) | Local-heuristic fallback from mesh state |
| Magic-wand 🪄 cue | Composer button | CaptainSummary → ControlOp | Captain on mac5 drafts; user reviews; user sends | If Captain down, pick any reachable pane |
| Autopilot toggle (per-chat) | ChatView header | AutopilotAgent | Background loop: idle-detect → cue → fill → send | Pause + banner on first rate-limit / 3 failed cues |
| Long-press send → "send & sleep phone" | Composer | ControlOp + NotificationTrigger | inject + register ntfy expectation | If ntfy not subscribed, no notification (silent) |
| Home Screen widget tile | PebbleWidget extension | WidgetGlance | App Group SQLite + `projection/latest.json` | Placeholder body on schema mismatch |
| Widget tap | systemSmall whole-widget URL | DeepLinkJump | `pebble://voice` quick-send to most-recent | Cold-launch opens emptyDetail |
| Widget row tap | systemMedium/Large `Link` | DeepLinkJump | `pebble://chat/<id>` push | Falls back to sidebar selection |
| ntfy push notification | iOS banner | NotificationTrigger | mac4 idle-watcher → ntfy.sh | If subscribed offline, batched on next foreground |
| Settings → Diagnostics | Settings sheet | WidgetGlance (local) | `CodexStrategyLog.shared` ring | None needed — local-only |
| Share extension (Wave 5, optional) | iOS share sheet from Safari/Messages | ControlOp | Drop into Pebble inbox → routes to most-recent | iOS-local; no mesh dependency |
| MeshControl power-user panel | Sibling iOS app | DataStream + ControlOp | Full topology, deploy, kill, batched inject | Pebble does not borrow this complexity |
| Siri Shortcuts (P5 candidate) | AppIntent | VoiceDictation | "Pebble, ask Captain where we are" | Falls back to opening Pebble |
---
4. The Autopilot Envelope — What Runs Without a Tap
The phone is the cockpit, not the runway. The system is in autopilot whenever it can be, and asks for a tap only when an EW invariant is genuinely at risk.
Tier A — Always autonomous (zero phone confirmation)
- Pane snapshot polling on every reachable machine
- Idle-watcher state machine on mac4 (BUSY→IDLE transitions)
- ntfy webhook fire on idle transition (cooldown 30s)
- App Group projection refresh on every state mutation (400ms debounce)
- Widget Timeline reload every 10 min + on writer mutation
- Captain absorbs context silently (per bootstrap prompt rule: "default to absorb")
- Pulse sessions executing within their EW gates
- Cortex skill promotions/demotions
- KARL trajectory mining
- All `DataStream` archetype retries
Tier B — Autonomous within a pre-armed envelope (phone-armed, then runs)
- Autopilot loop: Captain drafts N+1, Pebble fills composer, sends, repeats — until rate-limit, 3 failed cues, manual edit, or user toggle
- ops:autopilot sleep-loop with declared chain
- Share-extension intake → most-recent conversation
- Scheduled remote agents (cron-routines)
- Widget glance reflecting the autonomous work
Tier C — Requires phone confirmation (single tap)
- Send-text-prompt (composer commit)
- Switch-chat strategy when AX/Shortcut/URL ladder fails confidence threshold
- Toggle autopilot on/off
- Add/edit conversation
- Token rotation
- Cross-machine inject to a machine never reached before (one-time trust-on-first-use)
Tier D — Requires phone consideration (read a sheet, then tap)
- `Where are we?` summary review before next move
- Rate-limit "Reconfigure?" banner (swap Captain source, swap model, wait)
- Crash/restart banner after Pebble cold-start finds stale state
- ntfy notification with Captain-summarized body — Mohamed decides next move
Tier E — Hard-blocked, phone alone insufficient
Only one class: mac5 Captain bootstrap (tmux install + bootstrap-prompt paste). Once it ships, this class is empty.
The autopilot envelope's success metric: Tier C taps per active hour. Under 5 taps/hr = leisure. Over 20 taps/hr = the system is leaking into Mohamed's attention and a Tier A/B path is missing.
---
5. The Leisure Invariants — Mathematical Definitions
The system is in leisure state at time `t` iff all five EW invariants hold simultaneously. Failure of any one demotes to intervention state. The architecture must surface which invariant failed (banner + diagnostics), never just "something is wrong."
I₁ — Min Entropy (Information Budget)
`H_phone(t) ≤ H_max` where `H_max` is the cognitive load of a single iPhone screen.
Concretely: at most one WhereAreWeSheet visible, at most one autopilot banner, at most one notification per 30s cooldown, at most three diagnostic bars in a glance widget. Anything more = the phone has become a console, not a cockpit.
I₂ — Bounded Divergence (Track Discipline)
`active_tracks(t) ≤ 4` parallel work tracks at any moment.
Pebble V0.8 crucible bound: never exceed 4 parallel tracks. Same rule applies to the leisure OS as a whole. If Mohamed has more than 4 chat conversations actively pushing prompts, the system must collapse them via Captain summarisation, not multiplex them on the phone.
I₃ — Cross-Layer Forcing (Plane Coupling)
For every `ControlOp` issued from the phone, there must exist a corresponding `DataStream` or `NotificationTrigger` confirming the effect on the mesh.
`∀ op ∈ ControlOps : ∃ stream ∈ DataStreams ∪ Notifications : stream.confirms(op)`
No fire-and-forget that the phone can't observe. Sending a prompt without delivery confirmation = invariant violation.
I₄ — No Absorbing States (Always-Reversible)
For every UI state `s`, there exists at least one transition `s → s'` that does not require an external service.
Concretely: `/captain/ask` 404 → local-heuristic fallback. mac5 down → gateway stub. mac4 unreachable → "send to active" path. Widget schema mismatch → placeholder body. Empty mesh registry → emptyDetail hexagon. Every screen has an offline outcome. No screen can trap the user in "waiting for the mesh."
I₅ — Goal Coherence (Every Commit Advances Leisure)
For every commit `c` to any repo in the leisure OS:
`leisure_delta(c) > 0` measured as `Δ(Tier A+B autonomous coverage) - Δ(Tier C+D confirmations required)`.
If a commit increases the number of phone confirmations to reach the same outcome, it has anti-leisure delta. Such commits are bugs.
Today's Pebble HEAD `3803b76` has positive leisure_delta on every commit in the V0.8 ledger: P5 Wave 1 added projection (Tier A widget glance enables → reduces Tier C taps to check state), Wave 2 added widget (zero-tap state read), Wave 3 added deep links (collapsed multi-tap to one tap), Wave 4 added iPad split-view (eliminated push-pop friction on bigger screen).
---
6. The Operating System Narrative (One Page)
Mohamed leaves the house. Phone in pocket. Watch on wrist.
The home Macs are running. mac1 holds the gateway and the snapshotter. mac4 holds Codex and the idle-watcher. mac5 holds Captain (when the bootstrap is done). mac2, mac3, K11 hold whatever they hold. The mesh registry may be empty. Tailscale may be flaky. Partial-mesh is normal.
Mohamed sits down at a coffee shop. He opens Pebble. The sidebar lists his conversations, sorted by recency. The detail column shows the last one he was in. He glances at the widget on his Home Screen first — the last agent state is right there. No tap needed.
He has a thought. He long-presses the widget tile, dictates a sentence, and the phone sends it to the right Codex chat. mac4 takes the prompt, pastes it into Codex.app, and the idle-watcher arms. Forty seconds later his phone buzzes: Codex finished. The notification body says what Codex committed, not just "done." That body came from Captain on mac5.
He sips espresso. He taps the heart icon. The WhereAreWeSheet renders Captain's 5-line status: SHIPPED / BLOCKED / NEXT / OPEN-QUESTIONS / SCRATCH. He flips autopilot on for the Codex chat. Captain drafts N+1, Pebble fills the composer, Mohamed reads it, taps send. Loop runs for 25 minutes. He doesn't look at his phone again.
The phone buzzes: "Autopilot paused, Captain at limit." He taps "Switch source," picks the Pebble project Claude Code on mac1, autopilot resumes. He hasn't typed a full sentence in an hour. The Macs at home are doing the work. The phone is showing him what's true. The mesh is governing itself within the EW invariants. This is the leisure state.
When something genuinely breaks, the system tells him which invariant failed, gives him a one-tap escape route, and never traps him in a waiting screen. When nothing breaks, the phone is quiet. The work continues without him.
---
7. Architecture Anti-Patterns (Explicitly Forbidden)
These are the failure modes that would silently kill the leisure formula. The architecture rejects them at design time, not at PR review.
1. Mixed-archetype endpoints — e.g. a `ControlOp` that silently retries like a `DataStream`. Crucible spine forbids this for Pebble; the leisure OS extends the rule everywhere.
2. Phone-only state — any durable state that exists only on the phone and not on the mesh. Pebble's pebble.sqlite lives in the App Group; the App Group survives reinstall via the migration path; the source of truth is still the mesh.
3. Fire-and-forget without confirmation — every send must have an observable confirmation. I₃ enforces this.
4. Modal-trap screens — any screen that requires a network response to exit. I₄ forbids this.
5. Generic notification bodies — "Codex finished" is anti-leisure (one more tap to learn what happened). Captain-summarized body is pro-leisure.
6. Power-user creep into Pebble — topology views, deploy panels, batched inject. Those belong in MeshControl. Pebble is the leisure surface; MeshControl is the cockpit-for-cockpit.
7. Tier-C decisions disguised as Tier B — silent autopilot loops with no pause condition. AutopilotAgent must declare its stop conditions at registration.
---
8. Forward-Path Implications for Goal Conditioning
When Claude Code receives the goal prompt for the leisure-state, these are the operating principles to encode:
1. Default to autopilot. Tier A is the home base. Tier C is the exception. Every new feature must justify which tier it inhabits.
2. Default to fallback. No absorbing states. Every endpoint that can fail must have a non-network outcome.
3. Default to Captain. When the phone needs a summary, the answer comes from Captain if Captain is up, gateway-heuristic if not. Never raw mesh state surfaced to the phone.
4. Default to ntfy. When work completes without the phone in hand, the notification carries the meaning, not the event.
5. Default to deep link. Every glance must be one tap from action.
6. Default to projection. Widgets and glances read the on-disk projection, never the network directly.
7. Default to EW invariants as banners. When the system is degraded, name the failing invariant in the UI. Don't hide it.
The phone is the cockpit. The Macs do the work. The mesh holds the memory. The invariants hold the line.
That is the architecture.
Promotion Decision
Promote into a technical note or architecture paper with implementation anchors.
Source Anchor
leisure-goal-synthesis/04-architecture.md
Detected Structure
Method · Figures · Architecture