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

4.8 KiB

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?