# feat\(web,mobile\): Continue on… a linked environment · PR \#16758 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/16758)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: open

Source repository: pingdotgg/t3code

Source branch: t3code/peer/handoff-ui

Target branch: t3code/peer/handoff

Head commit: bae6ee0b2e3853df96ec3b279e68ca358169f2dc

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/16758)

[Commits](https://git.cafe/pingdotgg/t3code/pull/16758/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/16758/diffs?format=markdown)

## Description

---

Moving a thread to a linked environment ([#16751](https://github.com/pingdotgg/t3code/pull/16751)) was reachable only through the `t3_thread_handoff` MCP tool. Users had no way to start a move, see one waiting or under way, or reach the thread on the other machine afterwards.

## How

- **"Continue on…" on every thread entry point.**
  - Web: the sidebar thread menu, the chat-header thread menu, and the command palette. The menus read `threadHandoff.options` as they open, waiting at most 1.5 s, and show one entry per linked environment. A target that can't take the thread is disabled with the reason, for example "Not answering right now." or the missing project.
  - Mobile: the thread row's long-press menu, the iOS thread header's git menu, and an Android header action. These open a small "Continue on…" sheet that reads the options, because native menus are built ahead of time.
  - The entry needs the `peerLinks` capability, is hidden while a move is under way or done, and is disabled while the thread runs. A running thread moves only through the agent (`t3_thread_handoff` with `whenTurnEnds`).
- **The move's state above the composer**, from the shell's new `handoff` field, using shared wording (`threadHandoffNotice`):
  - pending: "Moving to <machine> when this turn ends", with **Keep it here**;
  - departing: "Moving to <machine>…";
  - departed: "This thread continues on <machine>", with **Open on <machine>**;
  - failed: the reason.

  While the thread is departing or departed, the composer is closed: web blocks sending with the reason, and mobile hides the composer behind the card.
- "Open on <machine>" shows only when this client is also connected to that environment, so it never navigates to a thread the client cannot load.
- `docs/user/remote-access.md` says where to find **Continue on…**.

## Verification

- Web `threadActionMenu.logic` (19 tests) covers the new submenu: one child per target, disabled with its reason, disabled while running, disabled for a connection that cannot change threads, and absent with no targets. Client-runtime `peerLinks` (5 tests) covers `threadHandoffNotice` for pending, departed, failed and no move.
- Mobile `thread-list-environments` (15 tests) still passes with the new `peerLinks` capability set.
- A real move through the sidebar menu across two dev servers, in the recording below.
- Web, mobile and client-runtime typecheck clean. Lint is clean on the changed files, apart from `ChatView.tsx`'s existing warnings.

## Screenshots

![Before (main) and after (this PR): the sidebar thread menu gains Continue on…, listing the linked environment](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/ca5ab8bc4acb3354/16758-before-after.png)

A real move from the sidebar menu. The thread's uncommitted edit to `greet.ts` arrived on the box unchanged, and the copy here became read-only:

https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/00bc9619b06ea4c2/16758-continue-on.mp4

![A departed thread: "This thread continues on cups", read-only, with Open on cups, and the send button disabled](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/a6990ac3d78fc1fd/16758-departed-banner.png)

![A failed move keeps the thread here and shows why: the box's branch has commits this one lacks](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/301d0001db812aaf/16758-failed-banner.png)

![The command palette's Continue on… lists the same targets](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/c95456d04fc025ec/16758-palette-targets.png)

![iOS: the departed card replaces the composer, with Open on cups (left); on the box, the moved thread's subtitle reads From T3 Code · cups (right)](https://gh-file-drop-api-prod-mi5fy3sowv63ufte.pinglabs.workers.dev/f/7286027478dce76d/mobile-pair.png)

The iOS long-press and header menus are native menus that the simulator automation could not open, so their "Continue on…" entries are not pictured.

Opus 5.5 via Claude Code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/pingdotgg/t3code/pull/16758" target="_blank"><picture><source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-devin-review-dark.svg?v=4"><img src="https://static.devin.ai/assets/gh-devin-review-light.svg?v=4" alt="Devin Review"></picture></a>
<!-- devin-review-badge-end -->
