- Walked: 2026-05-29 at
bf12b0f7d8. The retained result named an ephemeral/tmp/claude-oshun-lt-2-walkthrough/walk.mjsscript and reported reachable workspace URLs plus/v1/devices; it preserved no script, trace, request log, account pair, packet, or second-device receipt. - Reconciled: 2026-07-17 against the workspace route group and alias, packet builder and browser storage, Iris handoff contract, authenticated BFF route and durable snapshot authority, native Home fetch/consume path, fixture Library card, mobile navigation and Maestro contracts, device-token inventory, independent profile sync, and all five named Playwright files. The runtime reconciliation includes a three-process exact-production BFF and Chromium API proof, but not a fresh application or native-device run.
- Verdict: partial / deep for the shipped one-way seams — the current
runtime has a real desktop
PUT→ per-user BFF record → native HomeGETchannel, plus a same-browser localStorage path and strong workspace/deep-link evidence. The BFF now acknowledges only durable writes, enforces the exact contract's expiry, and has restart proof. The browser still does not await or display that acknowledgment, select a target device, persist the mobile return packet, reconcile concurrent edits, or drive the joined desktop → native mobile → desktop lifecycle in automation. - Current authority:
WALKTHROUGH/journeys/multi-device-workspace-handoff.mddefines the same one-way result and separates packet transport, browser restoration, device inventory, and profile sync.
Result at a glance#
| Interaction | Proof · limit |
|---|---|
| Historical route walk | Proof: the old walk recorded 200 responses for real /workspace/... and /app/<surface>/... URLs and a responding /v1/devices. Limit: its walker and raw evidence were ephemeral. A reachable route group and a registered-device list do not prove that workspace state moved between two devices. |
| Workspace frame | Proof: ten workspace-multi-panel cases cover auth gating, all eight panels, tab and note state, same-browser handoff capture/resume, constrained widths, tablet switching, exports, and local write failure. Limit: only a subset concerns handoff, and the displayed workspace combines live Nisaba notebook reads with a routed Veritas saved-article fixture in its broad desktop case. |
| Six-item packet | Proof: the shared contract deterministically normalizes reading, study, journal, voice, assistant, and notebook items, derives readiness, a token, oshun://handoff/... deep link, 48-hour expiresAt, and memory-write projections. Limit: the web builder embeds static continuity values, including userId: oshun-v1-workspace-user, fixed counts, consent, scopes, and active domain; it does not read the authenticated member's live Iris continuity state. |
| Desktop send | Proof: clicking Send workspace to mobile writes the serialized packet under oshun.iris.mobile-handoff.v1; a failed local write produces no captured badge and no network attempt. Limit: after local success, the browser launches an authenticated BFF PUT without awaiting its response. The visible timestamp records local capture even when auth, validation, size, network, or storage persistence fails. |
| BFF packet record | Proof: the BFF requires its durable sink, serializes candidate/save/publish mutations, awaits PUT/GET-touch/DELETE persistence, bounds canonical JSON at 256 KiB UTF-8, enforces exact-contract expiry plus 30-day retention, exports the exact subject, and joins signed continuity erasure with a digest fence. Five route cases, eleven store cases, a real-PostgreSQL integration case, and a three-process/two-restart Chromium API spec cover failure, isolation, recovery, consume, and adjacent preservation. Limit: semantic validation remains client-side, only one latest packet exists per user, and no device receipt is modeled. |
| Native Home receive | Proof: Home refresh calls the real GET, parses the shared contract, renders the card only for a valid packet, routes the primary item, and launches best-effort DELETE. Limit: delete failure does not block navigation or clear the local card, and no native automation seeds a desktop BFF packet then observes this fetched Home card. |
| Mobile Library specimen | Proof: component, navigation, contract, and Maestro evidence cover all six chips, resume routing, and the Send back to web control. Limit: the Library screen always calls buildMobileHomeIrisHandoffState() with authored Bhagavad Gita data; it is not the Home record fetched from the BFF. The Maestro flow explicitly expects Home's honest empty state, then exercises this Library fixture. |
| Links and narrow restoration | Proof: browser tests restore tab, panel, side panel, width, and selected content at 390 px. Limit: two link types are easy to conflate: packet deepLink is an oshun://handoff/<token> URI, while the tested browser state link is a relative /workspace/...?... URL. Exact selected evidence is restored only after the test copies desktop tab localStorage into the second browser context. |
| Mobile return | Proof: mobile can derive a correctly typed mobile_to_web packet, and web can apply such a packet when a test writes it into browser localStorage. Limit: both native Home and fixture Library Send back handlers open an assistant prompt. They do not PUT the return state, and web never fetches a return packet from the BFF. |
| Device and profile continuity | Proof: /v1/devices lists real durable device-token registrations, and one two-context profile spec proves profile/preference refresh after focus. Limit: neither authority is joined to handoff: the packet route has no device id, /v1/devices has no active workspace surface, and profile sync is not a workspace delivery receipt. |
Evidence map#
The vertical relay keeps the real one-way transport distinct from the two local test seams. Solid edges are shipped calls or exercised local transitions; dashed edges name the missing acknowledgment and return joins.
Proven observations#
The historical pass measured reachability, not handoff#
- The route correction in the old result was valid:
(workspace)is a Next route group, so/workspaceis public URL syntax, while/apprequires a[surface]segment. The current source still has(workspace)/workspace/[[...path]]andapp/[surface]/[[...path]]. /v1/devicesis also real today, but its meaning changed from the result's implied handoff prerequisite. It reads the durable device-token store and returns an honest empty array or registered platform/token rows. ItsactiveSurfacePathis alwaysnullbecause presence is not tracked.- The handoff route never calls that inventory. It keys one record by the
authenticated user id and has no target device, platform, push token,
presence, or acknowledgment field. The registry's former
/v1/devicessummary therefore described adjacency, not delivery. - The old generic
passis not retained. A route walk with zero page errors cannot establish the cross-device and round-trip outcome named by the result.
The workspace packet is typed but carries static identity context#
buildWorkspaceIrisMobileHandoffStatederives six items from the active tab, live reading anchor when available, selected evidence when available, and the current workspace state link. Honest fallback ids say no reading/evidence is selected rather than inventing a live record.- The shared
buildIrisMobileHandoffStatesorts those items, resolves required memory scopes, derivesready/limited/blocked, computes a deterministic token, adds a nominal 48-hour expiry, and builds the customoshun://handoff/<token>?direction=...&source=...URI. - Web does not supply live continuity.
WORKSPACE_IRIS_CONTINUITYhard-codes the user id, consumer, domain, consent, available scopes, five memory counts, and a 2026 timestamp. The active tab timestamp changeslastUpdatedAt, but it does not make the projection account-owned. buildIrisMobileHandoffMemoryWritesreturns write-shaped values for every non-blocked item. Neither the web send handler nor BFF packet route submits those values to the Iris memory write API. The 6 memory writes chip is a projection count, not six committed memories.
Local capture happens before cross-device delivery#
sendWorkspaceToMobilefirst serializes to the one browser key. Only a successful local write setsmobileHandoffSentAt, clears a locally injected incoming packet, and launches the network request.- The subsequent
fetchis a fire-and-forget authenticatedPUTwhose promise is discarded. Only network rejection is caught; a resolved400,401,413, or500is equally invisible to the component. - The visible copy says Iris state captured at..., which is narrowly true
for localStorage. It is not a BFF
storedAt, remote-device receipt, snapshot commit, or mobile render timestamp. - The failed-write Playwright case is useful negative evidence: a forced quota failure produces neither the key nor the badge and prevents the BFF request. No named browser case performs the inverse assertion—local success plus BFF failure—and verifies that another device receives nothing.
The BFF is a latest-packet mailbox with client-side semantics#
PUT,GET, andDELETE /v1/iris/mobile-handoffshare auth and abuse protection. The route binds the storage key torequest.authContext.userId, returnscache-control: no-storeon reads, and isolates an adjacent subject in the route test.- The validator accepts any plain, bounded canonical JSON object. It rejects
cycles, sparse arrays, non-finite values, prototype keys, excessive depth, and
payloads above 256 KiB measured as UTF-8. It remains intentionally opaque to
direction, embedded
userId, item identity, tenant, readiness, and route safety; Native Home performs shared-contract semantic parsing. - Production requires the shared snapshot sink and hydrates before routes open.
PUT, retention-touching GET, consume DELETE, retention purge, and subject
erasure all serialize a detached candidate, await its snapshot save, and only
then publish or acknowledge. Failure returns a stable
503without exposing the candidate, and the queue remains usable for retry. - Schema v1 strictly restores canonical entries and digest-only deletion fences,
performs one validated legacy
{ entries }migration, rejects future, malformed, duplicate, noncanonical, or resurrecting snapshots, and bounds the authority at 10,000 entries, 100,000 fences, and 64 MiB. - For the exact
oshun.memory.iris.mobile_handoff@1.0.0shape, the BFF enforces canonicalexpiresAtduring reads and the retention worker. All entries also use the continuity-state 30-day last-access window. The generic server does not otherwise claim full contract validation. - Customer continuity export includes only the authenticated subject's pending handoff. Signed continuity erasure removes that entry, preserves adjacent ids, and writes a SHA-256 digest fence that rejects stale recreation without retaining the raw subject id.
Native Home is the real consumer; Library is a specimen#
- Native Home requests the per-user record whenever
lastRefreshedAtchanges, parsesJSON.stringify(entry.state), and stores only a valid shared-contract value in component state. Fetch or parse failure results in the honest No cross-device handoff waiting branch. - Resume on mobile remembers a recognized domain, fires haptics, launches a best-effort delete, and routes using the packet token plus the item's mobile or web path. Navigation deliberately continues when delete fails, so replay remains possible on refresh.
- The shared mobile HTTP helper now omits
content-type: application/jsonwhen a request has no body. That lets the bodyless handoff DELETE reach Fastify's consume route instead of failing during empty-JSON parsing. - The Home handler does not mark the packet consumed in local state before routing, await delete, or confirm that embedded user data matches the authenticated subject. Exact-contract expiry is enforced by the BFF before a record reaches Home, although the client parser itself does not compare time.
- The Library tab is different. It always constructs a six-item mobile fixture with Bhagavad Gita reading, Metis study, Arete journal, Tara voice, assistant, and Nisaba notebook values. Its card proves presentation and route helpers, not a received desktop packet.
- The authored Maestro flow documents this distinction in comments: it asserts
Home's empty live state, switches to Library, inspects all six fixture chips,
and taps send-back into the assistant sheet. It never performs desktop
PUT→ native HomeGET.
A token deep link is not the tested workspace state link#
- The packet-level
deepLinkis always anoshun://handoffURI. Themulti-device-deepeningcase explicitly parses that protocol, host, direction, source, and token after a local send. - Workspace also renders a distinct relative state URL. It carries pathname,
active tab, panel, side-panel kind, and width. Item
routePathvalues can carry that URL, while the packet's top-leveldeepLinkdoes not encode the layout fields. - The 390 px two-context case captures the relative state URL, then explicitly
copies
oshun.workspace.tabs.v1from desktop into the second browser before navigation. URL state restores layout and panel; copied storage restores the selected evidence id. - The second context uses a browser with a mobile viewport, not the native Expo app. Its result proves responsive web restoration under shared browser state, not a platform deep link or account-synced content selection.
The return lane ends at an assistant prompt#
buildMobileReturnIrisHandoffStatecorrectly reverses the direction, updates timestamps, and rebuilds a typed token. Mobile navigation and contract tests exercise that pure transformation.- Native Home and fixture Library then calculate projected write count and open the assistant with Prepare Iris hand-off... copy. The handler does not PUT that packet, write Iris memory, mutate the received record, or publish an event for web.
- Web has no
GET /v1/iris/mobile-handoffconsumer. It reads amobile_to_webpacket only fromoshun.iris.mobile-handoff.v1during hydration or a same-originstorageevent. Component and Playwright tests inject that serialized state directly. - There is no revision, vector clock, merge policy, note conflict UI, replay fence, or acknowledgment linking mobile edits to the desktop tab. The shipped result is therefore one-way even though both directions exist in the type system.
The named automation proves several separate seams#
- The five named Playwright files contain 19 cases: ten broad workspace cases, two deep-link/Home-status cases, three tab/write-count/restore cases, one independent two-context profile-preference case, and three phased BFF API cases. Only portions of the broad workspace file touch handoff.
- The route has five focused cases; the authority store has eleven unit cases and one real-PostgreSQL restart/export/erase case. The phased browser proof used signed production auth against the exact built BFF across three clean processes: seed, recover-and-consume, then consumed-absence recovery. These establish real API durability, not native Home presentation.
- The shared Iris contract has four unit cases; mobile navigation has three, its coverage contract has two, and the card has one. Those tests establish packet mechanics and presentation, not a joined deployed lifecycle.
- The independent profile case is genuine two-context evidence for profile, theme, language, calendar, timezone, and document direction refresh. The Home continuity panel likewise proves profile refresh states. Neither reads the handoff mailbox or acknowledges workspace delivery.
- No named case starts with a real desktop workspace click, observes BFF
storedAt, opens native Home under the same account, resumes and consumes the exact token, changes state, sends it back through the BFF, and observes the desktop workspace merge it.
Boundaries and gaps#
- A local capture timestamp is not delivery acknowledgment. The UI never
awaits the BFF response or renders its
storedAt. - A projected memory write is not persisted Iris memory. The builder emits write-shaped data, but the handoff send path stores only one opaque packet.
- A static packet user id is not authenticated continuity. The embedded
oshun-v1-workspace-useris not reconciled with the BFF subject. - An opaque object cap is not contract validation. Direction, tenant, expiry, ids, routes, and embedded identity are client-trusted.
- Durable server acknowledgment is not visible delivery acknowledgment. The
BFF now saves before
200, but the browser discards that response and no target device confirms receipt. - A latest-user mailbox is not device delivery. There is no device target, queue, receipt, push wake-up, or per-device consume status.
/v1/devicesis not the handoff transport. It lists durable device-token registrations and is not joined to packet send or receive.- An
oshun://handofftoken is not a workspace state link. Responsive web restoration uses a separate URL and copied browser tab storage. - A mobile viewport is not the native app. The cross-context web test proves responsive hydration, not Expo routing or the BFF-fed Home card.
- A fixture card is not the fetched Home record. Library's populated card is authored locally; the Maestro flow's live Home branch is empty.
- A durable consume endpoint is not exactly-once device delivery. Native Home does not await it; network failure permits replay, and no local state transition confirms server consumption.
- A typed return packet is not a return channel. Send back opens an assistant prompt; it does not persist state for web.
- Profile refresh is not workspace acknowledgment. The independent multi-context sync and Home status panel use profile/preference authorities.
- A bounded digest fence is not a distributed identity lifecycle. The local partition fails closed at fence capacity and has no archive or multi-region compare-and-swap policy.
- Nineteen web/API cases are not a device round trip. No automation joins the exact token across desktop, native Home, consume, mobile edits, server return, and desktop merge.
Re-run evidence#
The reconciliation now includes focused runtime authority verification. It did not run a native Expo/Maestro device round trip. Retain the repository reports rather than another ephemeral walker:
pnpm --dir apps/oshun/web exec playwright test -c playwright.config.ts e2e/workspace-multi-panel.spec.ts e2e/multi-device-handoff-deepening.spec.ts e2e/multi-device-deepening.spec.ts e2e/profile-preference-sync.spec.ts --project=chromium --workers=1
pnpm --dir apps/oshun/bff exec vitest run src/routes/iris-mobile-handoff.test.ts
pnpm --dir apps/oshun/bff exec vitest run src/routes/iris-mobile-handoff-store.spec.ts
OSHUN_ADMIN_DATABASE_URL=<postgres> pnpm --dir apps/oshun/bff exec vitest run -c vitest.integration.config.ts src/routes/iris-mobile-handoff-durability.integration.spec.ts
IRIS_MOBILE_HANDOFF_E2E_PHASE=<seed|recover|consumed> E2E_BFF_URL=<exact-bff> pnpm --dir apps/oshun/web exec playwright test e2e/iris-mobile-handoff-authority-bff.spec.ts --config playwright.fast.config.ts --project=chromium --workers=1
pnpm exec vitest run libs/oshun/memory-iris/src/mobile-handoff.test.ts
pnpm --dir apps/oshun/mobile test -- --runInBand src/navigation/mobileIrisHandoff.test.ts src/navigation/mobileIrisHandoff.contract.test.ts src/components/MobileIrisHandoffCard.test.tsx
pnpm --dir apps/oshun/mobile test -- --runInBand src/network/http-client.test.ts src/network/oshun-bff-client.test.ts
Retain the commit and environment, authenticated web/mobile subject ids, the
desktop packet and top-level token, local capture timestamp, BFF response and
storedAt, snapshot tier and restart read-back, native fetch/parse/card state,
consume result, exact route, and any return packet. A round-trip-grade run must
also retain a deliberate mobile edit, server-side return receipt, desktop
read-back, conflict decision, and the final state on both devices. The BFF's
separate restart, export, erasure, retention, and digest-fence evidence should
remain attached to the same release proof.
Source trail#
- Current journey contract
- Workspace route group
- Canonical
/app/[surface]alias - Workspace packet builder and local storage
- Workspace send and local resume handlers
- Shared Iris packet contract
- Per-user BFF mailbox
- Durable mailbox authority
- BFF snapshot wiring
- Native Home fetch, resume, consume, and send-back prompt
- Native BFF client
- Native body-aware JSON transport
- Mobile packet navigation helpers
- Mobile handoff card
- Fixture Library consumer
- Registered-device inventory
- Broad workspace browser evidence
- Two-context web restoration and Home status evidence
- Tab, token, write-count, and storage restoration evidence
- Independent profile/preference sync evidence
- Exact-production restart/consume API evidence
- BFF mailbox route evidence
- BFF authority unit evidence
- BFF real-PostgreSQL evidence
- Shared contract evidence
- Mobile card fixture evidence
- Authored native flow
Cross-references#
- Workspace catch-all view — per-route workspace state, local persistence, and responsive panel behavior.
- Customer Home view — independent profile/preference continuity panel, not handoff delivery authority.
- Search-to-library journey — upstream saved-content flow that can open a workspace.
- Account deletion and DSAR result — the continuity export/erasure composition now includes this mailbox partition.
- Offline-first result — the next resilience result; service-worker continuity is separate from cross-device delivery.
- Results convention — status vocabulary and evidence rules.
Open questions#
- Which live Iris continuity read will replace the static packet subject, consent, scope, domain, and memory counts?
- Should the BFF validate the shared contract and bind embedded subject/tenant to auth before storing routes and summaries?
- Will Send workspace to mobile expose captured locally, stored, and received as distinct states with retry and accessible failure copy?
- How will a target device be selected from the real device-token inventory, and what delivery/read receipt will it publish?
- How should the web surface the BFF's durable
200, stable failure reasons, and retry state instead of discarding the response? - What replay/receipt protocol should sit above the serialized latest-write and durable consume operations to provide device-level exactly-once semantics?
- Will native Home clear local card state only after an awaited consume, and how will it recover from delete failure without accidental replay?
- What route will accept
mobile_to_web, and how will web fetch, consume, and merge it with concurrent tab and note updates? - What archive or identity-reuse policy should govern the bounded digest fences after the local partition has erased the customer record?
- What native automation will prove the exact desktop packet on Home, a mobile edit, server return, desktop merge, and final equality after process restart?