Skip to article
PlanToCodeDocsGet the app

HandbookStart here

What PlanToCode is

A desktop app that runs OpenAI Codex on your projects with every command, file, and diff one click away, plus iPhone and Android companions that drive the same session through a relay. This handbook explains how it is built.

Checked against the source on 17 September 2026

On this page

What is different

PlanToCode is a Mac and Windows app that runs OpenAI Codex on your ChatGPT subscription against a local project folder. This handbook explains the parts you cannot see from the screen: which process owns what, how a message becomes a Codex turn and gets confirmed, how the timeline is assembled from history pages and a live overlay, what the relay does and refuses to do, and how the bundled tools reach your Chrome, Gemini, and your phones.

A message stays on the phone until the desktop’s receipt, and in the outbox until Codex echoes it
A message stays on the phone until the desktop’s receipt, and in the outbox until Codex echoes itA message sent from a phone passes through the relay to the desktop, which saves it as an outbox entry and returns a receipt to that phone, and only then does the phone drop its own copy. The desktop starts a Codex turn with the operation ID as the client message ID and removes the outbox entry when Codex echoes that ID back. The relay only passes messages through: it keeps no queue and no replay log. Timeline updates then reach phones that hold an interest lease, and the phone’s row carries the same operation ID. If the reply to turn/start is unclear or the desktop restarts before the echo, the desktop looks for the operation ID in the session history.
PhoneiPhone or Android
Relayregional server
DesktopRust runtime
Codexchild process
keeps its own copy
keeps nothing: no queue, no replay
outbox entry · operationId
turn runs · items recorded
rpc.request · idempotencyKey
saved in SQLite
receipt, only to this phone
copy dropped
turn/start · clientUserMessageId
item/completed · userMessage.clientId
sent: entry removed
chat:timeline-updated, only to phones with a lease
row with the same operationId
Unclear reply to turn/start, or a restart before the echo: the desktop searches the session history for the operationId. Found: sent and removed. Missing: the entry is held and asks you to check the timeline before sending again.
  • ReceiptqueueId · operationId

    Returned as soon as the message is saved in the desktop outbox, and only to the phone that asked. The phone waits up to 20 s for it and keeps its own copy until it arrives.

  • Codex echouserMessage.clientId

    The drain task sends the operationId as clientUserMessageId. When the live item/completed notification carries it back, the desktop records the message as sent and removes the outbox entry.

  • No desktop connected-32011 · -32010

    The relay answers the phone itself: -32011 while the desktop is reconnecting, -32010 once it is offline. Nothing is queued, and the phone keeps its copy.

Three rules shape everything below

  • The desktop is the authority. It owns the project files, the Codex app-server child process, the durable outbox, and the RPC router that phones talk to.
  • The relay is dumb on purpose. It routes envelopes between authenticated devices and holds no project contents, no durable queue, and no replay log.
  • Every timeline row is a typed object with a stable identity, so the same conversation renders identically on the desktop and on a phone without text matching or timestamps deciding what exists.

Where to read