Codex Desktop: atomic project-aware create_thread with runtime roots and idempotency

Open 💬 0 comments Opened Jul 31, 2026 by daragao3

What variant of Codex are you using?

Codex Desktop on Windows.

  • Codex Desktop: 26.721.11231.0
  • Codex CLI/app-server: 0.145.0

What feature would you like to see?

Please extend the Desktop-owned create_thread tool so project assignment,
runtime workspace roots, and retry identity can be supplied and proven in one
atomic operation.

Proposed request:

{
  "prompt": "<initial prompt>",
  "target": {
    "type": "project",
    "projectId": "<saved local project id>",
    "environment": {
      "type": "local"
    }
  },
  "runtimeWorkspaceRoots": [
    "<saved project primary folder>",
    "<additional exact source folder>"
  ],
  "idempotencyKey": "<stable caller-owned logical operation id>"
}

Required ready response:

{
  "threadId": "<exact durable task id>",
  "hostId": "local",
  "projectId": "<saved local project id>",
  "cwd": "<saved project primary folder>",
  "runtimeWorkspaceRoots": [
    "<saved project primary folder>",
    "<additional exact source folder>"
  ],
  "idempotencyStatus": "created"
}

A same-key/same-arguments replay should return the same task with
idempotencyStatus: "replayed". A same-key/different-arguments replay should
return a deterministic conflict without creating or mutating a task.

This belongs at the Desktop boundary rather than generic app-server
ThreadStartParams: Desktop owns saved project IDs and persists the
project-to-thread assignment, while app-server owns the underlying thread.
Desktop is therefore the only layer that can atomically prove both sides.

Why the existing APIs are insufficient

The current exposed Desktop create_thread schema accepts prompt, target,
model, and thinking. It can create a thread in a saved project, but it
cannot accept ordered runtime roots or a caller-owned idempotency key, and its
ready result does not promise effective project/cwd/root proof.

Generic app-server thread/start can accept runtime configuration, but a
thread created there is not assigned to the Desktop saved project. This has
also been observed for thread/fork: a child of a correctly project-bound
Desktop thread can still be projectless.

Live canary evidence

I tested the most conservative two-stage alternative:

  1. Desktop create_thread created exactly one task in an existing saved local

project.

  1. The exact returned thread ID was durably retained.
  2. Desktop inventory proved the expected local host, saved project, and

project-root cwd.

  1. Exactly one app-server thread/resume was sent for that same ID with the

project root and one additional source directory in
runtimeWorkspaceRoots.

The definite resume response preserved the exact task ID and cwd, but returned
no runtime-root proof. A fresh thread/read exposed cwd but no runtime-root
field. Desktop still showed the correct project, so project association
survived, but the applied root set could not be verified.

The caller failed closed, archived the disposable canary by its exact ID, and
did not fork or replacement-create.

Required ownership and recovery semantics

Desktop should:

  1. validate the saved project, local host, canonical project folder, and

ordered absolute roots before thread creation;

  1. reserve the idempotency key with a canonical request fingerprint;
  2. create one app-server thread using the project folder as cwd and the

requested ordered runtime roots;

  1. persist the saved-project assignment for the exact thread ID;
  2. verify effective host/project/cwd/roots before returning success; and
  3. persist the idempotency result across Desktop restart.

If app-server may have created the thread but Desktop loses the immediate
response, replay of the same key must reconcile the original identity. It must
not replacement-create because the first result was ambiguous.

The placement fields in the response need to be effective read-back evidence,
not an echo of the request.

Suggested acceptance tests

  • Existing calls without the new fields remain backward compatible.
  • Valid project-aware creation returns exact project/cwd/ordered-root proof.
  • Same key and same canonical arguments invoke app-server creation once and

always return the same task ID.

  • Same key and different arguments return a deterministic conflict.
  • Matching concurrent calls converge on one task.
  • The idempotency record survives Desktop restart.
  • Invalid project or roots create no task.
  • Response loss followed by replay resolves to one task.
  • Failure after thread creation retains the exact identity for reconciliation.
  • Crash injection at each transaction boundary converges to either no task and

a retryable reservation, or one fully placed task.

  • The returned task is visible in both the saved project and global Recents

within the product's normal indexing bound.

Additional information

This is adjacent to #25498, but narrower. That issue requests registering
projects and moving threads between projects. This request assumes the saved
project already exists and focuses on deterministic initial creation:

  • one saved-project assignment;
  • one exact runtime root set;
  • one durable retry identity;
  • one placement-proof response;
  • zero duplicates after ambiguity or restart.

This also intentionally avoids direct mutation of Desktop state databases and
does not require exposing Desktop-private project IDs in the generic Rust
app-server protocol.

View original on GitHub ↗