Remote phone session appends to same thread while open CLI/TUI does not ingest remote events

Open 💬 3 comments Opened May 16, 2026 by monitz87
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of Codex CLI is running?

0.130.0

What subscription do you have?

Max (200)

Which model were you using?

gpt-5.5 (high reasoning)

What platform is your computer?

Darwin 25.2.0 arm64 arm

What terminal emulator and version are you using (if applicable)?

cmux 0.64.6 (86) [2f96c15c2]

Codex doctor report

not available

What issue are you seeing?

I had a Codex CLI/TUI session open on my machine. I opened the same session
from my phone. The phone session appended user messages and agent work to the
same thread. The CLI/TUI remained open the whole time, but did not receive or
integrate those remote events into its live context. When I continued typing
in the CLI/TUI, it appended a second continuation from stale in-memory context
to the same JSONL transcript.

What steps can reproduce the bug?

Uploaded thread: 019e199a-3d58-7863-b06e-c84a99bd9a87

What is the expected behavior?

Expected:
If a remote client attaches to an existing live CLI/TUI session, remote events
should either:

  • stream back into the CLI/TUI and update its live context, or
  • be rejected/locked/forked explicitly so two clients cannot append divergent

continuations to one linear transcript.

Actual:
Both paths were appended to the same transcript file linearly, but the CLI-
side model context did not include the phone-side segment.

Additional information

_No response_

View original on GitHub ↗

3 Comments

github-actions[bot] contributor · 3 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #21743
  • #21551

Powered by Codex Action

Snailflyer · 3 months ago

This is a useful failure mode to call out. The dangerous part is not just that the phone can append to the same transcript; it is that the live CLI/TUI process still has stale in-memory context, so the thread looks linear on disk while the active controller is effectively forked.

For a remote attach design, I would expect one of two explicit contracts: either the local live process remains the authority and ingests every remote event before continuing, or the remote client gets a hard fork/lock indication so two controllers cannot write divergent continuations.

I built Faryo as a narrow open-source experiment around the other model: keep Codex CLI running inside tmux as the single live process, and let phone/browser act as a thin control surface for compact output, short input, approvals/interrupts, and handoff. It is not a fix for Codex mobile, but it may be a useful comparison point for the "same live session, no second transcript writer" case: https://github.com/Snailflyer/faryo

amosaikman · 3 months ago

A code-path trace seems to line up with this failure mode.

The exit/resume behavior follows from the current TUI path: thread/resume returns reconstructed thread.turns, and the TUI replays those turns into the chat widget. A still-running CLI/TUI session does not appear to tail or reload the thread store. turn/start resolves the already-loaded CodexThread and submits Op::UserInput against that in-memory session state; it does not refresh persisted history first.

That makes the problematic sequence:

  1. CLI/TUI has a thread loaded with in-memory context.
  2. Phone/mobile appends turns to the same persisted thread through another path.
  3. CLI/TUI remains visually and model-context stale.
  4. User types in CLI/TUI.
  5. The new CLI turn appends to the same transcript, but was generated without the mobile segment in context.

Perhaps the primary fix could be a live-session ownership invariant: while a thread is loaded, mobile/remote turns for that thread route through the same loaded app-server CodexThread and stream events to all subscribed clients.

If that cannot be guaranteed for every path, a defensive guard could be added before accepting turn/start: compare the loaded thread's expected store revision/watermark against the current persisted thread revision, and reject with a refresh-required conflict when the thread changed externally. That would prevent appending from stale in-memory context.

updated_at alone may not be a strong enough correctness boundary because it is metadata/timestamp based and can be coalesced. A real opaque thread revision/watermark would be safer; for the local rollout store, an append-only file watermark such as byte length plus mtime/hash could be a practical starting point.