cocottetech/@platform/codebase/@features/ai-copilot/docs/cross-device-handoff.flow.md
natalie 1b719e1fd7 chore(bootstrap): initial V4 commit
Clean successor to V3 (forge: lilith/atlilith). Seeded from local Mac
working tree at ~/Code/@projects/@cocottetech/. node_modules and build
artifacts excluded via .gitignore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 08:11:41 -07:00

76 lines
4.8 KiB
Markdown

# Flow — cross-device handoff (iPhone ⇄ laptop / web)
## Goal
Quinn does most of her work on iPhone (chat-driven), but bulk operations and calendar work feel better on a laptop. The handoff between **CocotteAI on iOS** and the **web FEs on `ai.cocotte.maison`** (brief G) must feel like one continuous session, not two apps to keep in sync.
## Constraints
- Same auth on both surfaces (SSO device-link → shared session).
- platform.api is the single source of truth; both clients read from it.
- Cache invalidation arrives via Redis pub/sub on vps-0 → SSE / WebSocket to clients (brief G open question; resolve here as: WebSocket from vps-0 cache layer).
- No conflict-resolution UI for P0 — last-write-wins via platform.api row `updated_at`. Concurrent edits to the same resource are extremely unlikely (single Quinn).
## Three handoff modes
### Mode 1 — Pick-up-where-you-left-off
Quinn is editing a caption draft on iPhone; her phone dies / she switches to laptop.
- Web companion at `ai.cocotte.maison` opens → ai-copilot greets her.
- A subtle banner at the top of chat: "Picking up — last edit on iPhone 2 min ago: caption for OF post #abc..."
- Tap → opens the same edit drawer the iPhone was showing.
- Pending mutations from iPhone (queued in SyncEngine if offline) reconcile in the background; if any failed, a notification surfaces in chat.
### Mode 2 — Sidekick-on-laptop
Quinn is on iPhone but wants the bigger screen for a specific task (calendar review, asset library batch operation).
- In iPhone chat, she types: "**Open the calendar on my laptop.**"
- ai-copilot returns a card: "Calendar opened on Desktop · transquinnftw-MBP."
- (Detected by: device-link list shows active web sessions.)
- The card on iPhone shows what the laptop is doing in real time (current month, scroll position).
- Laptop opens to the calendar drawer directly (deeplink).
- Mutations on either device propagate via cache.invalidate → both UIs update.
### Mode 3 — Notification chase
Push lands on iPhone; Quinn is mid-something on laptop and doesn't want to switch.
- iOS notification arrives normally (brief C / notification-rich-preview).
- Quinn presses ⌘+T on laptop (or clicks the notification badge in `ai.cocotte.maison`) → the same approval card appears in the web companion's chat.
- Action taken on laptop → push notification on iPhone auto-clears.
- Audit row records *which device* approved (in `outcome_json.device_id`).
## State sync model
- **Authoritative state**: platform.api on black. Both clients always defer to it.
- **Optimistic state**: each client's local cache. Mutations are committed locally + queued for platform.api.
- **Sync channel**: WebSocket from vps-0 (subscribing to Redis cache.invalidate); each connected client gets push notifications of every change. iOS uses APNs for backgrounded delivery; web uses native WebSocket while tab is open.
- **Reconciliation**: on reconnect, client fetches `updated_at > last_seen` deltas from platform.api.
- **Conflict policy** (rare): last-write-wins by `updated_at`. If a mutation's `if-match` fails, show a non-blocking toast "Refreshed from server — your version overwrote a newer one. View diff?" — tapping opens diff card.
## Visible affordances
### On iPhone chat
- **"Send to laptop"** action on any drawer (sheet header overflow menu). Opens the same drawer on the laptop session.
- **Active sessions indicator** in settings: which devices are currently connected, last seen, "sign out everywhere else" affordance.
- **Banner** when iPhone resumes from background and detects activity from another device.
### On web companion
- **"Send to phone"** action on any drawer — pushes a notification to iPhone with deeplink.
- **Device list dropdown** in the top-right (mirrors iOS settings session list).
## States to design
- Pick-up banner (iPhone → web).
- Pick-up banner dismissable / persistent.
- Sidekick-on-laptop card (iPhone view of laptop activity).
- Notification cleared cross-device confirmation (subtle toast on iPhone after web takes action).
- Conflict toast + diff card.
- Active sessions list (settings).
- Offline-on-one-device-online-on-other state (e.g. iPhone offline, laptop edits) — laptop edits succeed, iPhone catches up on reconnect.
## Out of scope
- iPad as a third active device (P5+).
- Concurrent collaboration with another operator (still single-Quinn P0).
- macOS Catalyst (the web companion is the laptop story for P0).
## Open questions
- WebSocket vs Server-Sent Events from vps-0? WebSocket is bidirectional but heavier; SSE is one-way and fine for cache invalidation but needs a second channel for client→server pings.
- "Send to laptop" — should it always open the *same* drawer, or open at the parent feature (e.g. calendar root, not specific tour edit)?
- Audit log device attribution — show always or only on demand?