First slice of the phone's sessions work: real sessions in the shell, and opening a session to a read-only live thread. Sending, interrupt, delete, rename and settle, and the designed session page, are later slices.
What changes
The home reads the paired environment. src/home/source.ts now takes the shared sidebar providers (SidebarDataContext, SidebarSessionsContext, useSidebarDrafts) and normalises them into the shell's shapes. The workspace groups become one list of rows that each know their workspace. Every status, title and settle passes through as the environment served it: done and ready are statuses, read by the shared sessionBusyTone. The environment line comes from the phone's controller: the saved label, the route in use (LAN or tunnel), and whether it is connected. The Inbox badge, tab counts and search follow, because they all read the same source.
Drafts. PhoneEnvironmentProvider now gives the application providers a draft page (/draft/<id>). Only hosts with draft pages get the shared draft cards, so this is what makes the Drafts tab list the environment's real drafts.
The fixture. It is now the source only when nothing is paired and the build sets EXPO_PUBLIC_FIXTURE=1. Otherwise an unpaired phone shows the empty home.
A session opens read-only. src/session/useReadOnlySession.ts opens a session the way web's session route does:
- It sends
session.openonce the socket is up. - The snapshot and then the live events come through the shared active-thread provider (
environment-thread). - When the page goes, it calls
setActiveSession(null), which releases the session's subscriptions. - It never asks for
session.history. - A
donesession on screen is acknowledged on the environment (acknowledgeSessionDone), so tapping a Done row in the Inbox clears it there. Nothing is cleared locally, and a clear that did not land is asked again. - The page owns the client's one active session only while it is the page in front (
useIsFocused). It opens the session on focus and releases it on blur or on leaving, but only if the session is still its own. - An open that a dropped connection cut off is asked again on the next connect.
- An environment that does not advertise
subscription.replayis never opened: without replay, the shared client fills the transcript fromsession.history. The page says the environment needs updating instead.
The page. src/session/SessionThread.tsx has a back icon, the title and project, and the status line (the home's mark and line). The transcript is a FlatList that follows its end while the turn streams, unless you scroll up; a glass button brings you back to the latest. Each row reads its own message from the shared stores, so a streamed token re-renders only that row. Rows cover:
- markdown
- reasoning, collapsed
- bare tool rows: the tool's name and status
- images, fetched through
fetchArtifact
There is no composer yet: a glass bar at the bottom says the session is read-only on the phone for now. If the session cannot be kept current (it failed, is gone, or the environment is too old), a fill above that bar says so over the transcript, with a retry icon. A child-session row opens the child the same way. With the fixture, the page renders a thread recorded from a live run (src/session/recorded-thread.ts).
Removed, because nothing used them any more:
- the Convex-era data hooks (
src/data/*) - the old session screen, with its
Composer,PermissionBanner,lib/dialogsandStatusDot SessionCard- the session page placeholder
No server changes.
Checks
tests/home-source.test.ts(12 tests) covers the source's normalisation:- every status passed through (
done,ready,busyand others) - row identity
- child nesting across groups
- unavailable workspaces
- missing titles and times
- drafts
- when the fixture applies
- the environment line
- every status passed through (
tests/read-only-thread.test.tsx(13 tests, jsdom) mounts the shared providers over the mock environment client and checks:- the snapshot
- a turn started elsewhere, streaming into the rows
- the acknowledgement
- a child session
- the release on leaving
- draft cards
- a session deleted while on screen
- the page in front owning the session (blur, refocus, a page under another)
- an environment without replay, at open and after a reconnect
- an open cut off by a dropped connection
- a retried acknowledgement
- that
loadSessionHistoryis never called
live/sessions.live.tsruns the same hooks against a real server:- It connects through the phone's controller, and the home's rows match the server's sessions and draft.
- It opens a session and streams two live Claude Code turns into it (a Read, then a shell command, reasoning and a reply).
- It leaves, and the session is unsubscribed.
- On the wire: one
session.open, zerosession.history.
- The web export was screenshotted in dark and light: the fixture home, the read-only page from the recorded thread (including scrolled up, with the button), and the unpaired empty home.
npx expo export --platform androidbundles.
The Android glass check on a device is still open from the shell PR.
🤖 Generated with Claude Code
Activity
11[Medium impact] The changes affect the paired mobile session workflow, from the home list through viewing a live session.
Fix the hidden failure and retry state before merging.
Findings
Summary
The phone now reads real sessions and drafts from the paired environment and opens sessions as read-only live threads.
- The phone home shows sessions and drafts from its paired environment.
- Opening a session shows its read-only live thread.
- Session transcripts render live messages and follow new content.
- Mobile provider tests use the same React setup as the phone.
Scope acknowledged by calledforth: sending, interrupt, delete, rename, settle, and the designed session page are deferred to later slices. The Android glass check on a device remains open.
Diagram
sequenceDiagram
participant Phone as Phone session page
participant Client as Environment client
participant Server as Environment
participant Stores as Shared thread stores
Phone->>Client: openSession(sessionId)
Client->>Server: session.open
Server-->>Client: Snapshot
Client-->>Stores: Messages and content
Server-->>Client: Live events
Client-->>Stores: Updated row content
Stores-->>Phone: Render changed rows
Phone->>Client: setActiveSession(null) on leaving
Client->>Server: UnsubscribeReviews (1) · Last reviewed commit: "Show the environment's sessions on the p..." · Reviewed by Greptile
Your organization has used all 50 credits included in the free plan this billing period. To keep receiving reviews, upgrade your plan.
🚀 Deploying Preview to Cloudflare 🚀
Preview Deployments by commit
View logs ↗
View logs ↗
View logs ↗
View logs ↗
View logs ↗