Native goal continuation emits UUIDv4 turn IDs despite the app-server UUIDv7 contract

Open
#37,056 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
rust

Research direction

Start in codex-rs/ext/goal/src/runtime.rs, then trace try_start_turn_if_idle into codex-rs/core/src/session/inject.rs and its new_default_turn_with_sub_id call. Read codex-rs/core/src/tasks/regular.rs to confirm how TurnContext.sub_id reaches turn/started and the other lifecycle events. Done means native goal continuations expose one stable UUIDv7 turn ID across those events, metadata, and control APIs.

Written by the indexing model from the issue text.

Description

app-server bug CLI

What issue are you seeing?

With native goals enabled, an idle thread with an active goal can start an automatic continuation whose public app-server turn ID is UUIDv4.

The app-server Turn contract explicitly says: Codex-generated turn IDs are UUIDv7. However, the automatic goal path calls try_start_turn_if_idle, which currently creates its TurnContext with Uuid::new_v4(). The resulting UUIDv4 is exposed by turn/started, item/*, turn/completed, persisted turn metadata, and active-turn control RPCs.

This is observable without sending another turn/start: the goal-created turn itself already violates the public ID contract. Clients can rely on the documented UUIDv7 property for timestamp ordering and correlation, so the difference is not only cosmetic.

Environment:

  • Codex CLI: 0.146.0
  • Platform: Darwin 25.5.0 arm64
  • Also present on public main at ed2f985a26eee9a59cde0fdefd20f69b45bc25f5

What steps can reproduce the bug?

  1. Start app-server with native goals and the experimental API enabled.

  2. Initialize a materialized thread.

  3. Set an active goal with thread/goal/set, for example:

    {
      "method": "thread/goal/set",
      "id": 3,
      "params": {
        "threadId": "<thread-id>",
        "objective": "Inspect the workspace and report the result.",
        "status": "active"
      }
    }
    
  4. Leave the thread idle so the goal runtime starts its automatic continuation.

  5. Read the next turn/started notification.

  6. Inspect params.turn.id. Its UUID version nibble is 4, not 7.

The current source path is:

  • codex-rs/ext/goal/src/runtime.rs: active goal continuation calls thread.try_start_turn_if_idle(...).
  • codex-rs/core/src/session/inject.rs: try_start_turn_if_idle calls new_default_turn_with_sub_id(uuid::Uuid::new_v4().to_string()).
  • codex-rs/core/src/tasks/regular.rs: turn/started exposes that TurnContext.sub_id as the lifecycle turn ID.

What is the expected behavior?

Every Codex-generated turn ID exposed through app-server should be UUIDv7, including turns started automatically by native goal continuation.

The turn/started, item/*, turn/completed, persisted metadata, and active-turn control APIs should continue using one stable ID, but that ID should satisfy the documented UUIDv7 contract.

Additional information

Related: #36866 reports a separate correlation defect when a later turn/start returns a provisional UUIDv7 submission ID while input is steered into an already-active goal turn. This report is narrower and independently reproducible: the native goal continuation produces a UUIDv4 public turn ID even when no later turn/start is sent.

Dominant language
Rust
Stars
125k
Forks
19.5k
Avg merge
1m
Merged PRs (30d)
1k

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from openai/codex

All issues in openai/codex

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.