Skip to content

Codex Desktop project rename updates label but leaves workspace root and cwd metadata stale #22075

@clockworksquirrel

Description

@clockworksquirrel

Summary

Codex Desktop can put a project into a split state after using the project rename UI:

  • the sidebar display label changes
  • the underlying workspace root remains ~/Documents/New project N
  • the actual folder on disk remains ~/Documents/New project N
  • existing thread rows and rollout metadata continue to reference the old root

The visible result is a sidebar row that shows both the renamed project label and the old autogenerated folder name, for example:

Vera Companio...    New proj...

This was reproducible across multiple renamed projects in the same profile, not just one project.

Environment

Observed on:

Codex Desktop: 26.506.31421
Bundle id: com.openai.codex
Platform: macOS arm64

The public repository checked was openai/codex at:

2abdeb3 Read cached metadata for installed Git plugins (#20825)

Why I am filing this here

I am filing this in openai/codex because it is the public repo linked to Codex installs and it contains the app-server, protocol, thread-store, rollout, and state-db code that participate in this bug. However, the Desktop/Electron code that appears to own the project sidebar and global Desktop state does not appear to be present in this public repository.

The public repo README describes this repository primarily as the Codex CLI and points separately to the Desktop app experience:

<strong>Codex CLI</strong> is a coding agent from OpenAI that runs locally on your computer.
...
If you want the desktop app experience, run <code>codex app</code> or visit ...

The installed macOS Desktop app is a packaged Electron application. Extracting the shipped /Applications/Codex.app/Contents/Resources/app.asar shows this package metadata:

{
  "name": "openai-codex-electron",
  "productName": "Codex",
  "version": "26.506.31421",
  "main": ".vite/build/bootstrap.js",
  "codexBuildFlavor": "prod",
  "codexBuildNumber": "2620"
}

The Desktop-specific persisted state keys involved in this bug were present in ~/.codex/.codex-global-state.json and in the shipped Electron bundle, but I could not find them in the public repo checkout. These searches returned no matches in openai/codex:

rg 'electron-workspace-root-labels' openai-codex-src
rg 'electron-saved-workspace-roots' openai-codex-src
rg 'thread-workspace-root-hints' openai-codex-src
rg 'project-order' openai-codex-src

The missing keys matter because the observed split state is created around those Desktop-owned values:

electron-workspace-root-labels      old root -> new display label
electron-saved-workspace-roots      still contains old root
project-order                       still contains old root
thread-workspace-root-hints         can still point at old/projectless roots

So this issue likely needs Desktop/Electron source changes, not just Rust changes in this repo. If that source lives elsewhere, please route this to the Desktop owner. The public Rust code is still relevant because it explains why stale rollout cwd values can survive and reappear after a partial Desktop-side rename/remap.

Steps to reproduce

  1. Create or open several Codex Desktop projects that were auto-created as ~/Documents/New project N.
  2. Create at least one thread in each project.
  3. Rename the projects from the Desktop UI, for example:
~/Documents/New project 3 -> Hermes agent
~/Documents/New project 4 -> Pinchpoint gateway
~/Documents/New project 5 -> Vera Companion app
  1. Restart Codex Desktop.
  2. Observe the project sidebar.

Observed state before repair

The display labels were updated, but the durable roots were not.

{
  "electron-workspace-root-labels": {
    "/Users/<user>/Documents/New project 3": "Hermes agent",
    "/Users/<user>/Documents/New project 4": "Pinchpoint gateway",
    "/Users/<user>/Documents/New project 5": "Vera Companion app"
  },
  "electron-saved-workspace-roots": [
    "/Users/<user>/Documents/New project 5",
    "/Users/<user>/Documents/New project 4",
    "/Users/<user>/Documents/New project 3"
  ],
  "project-order": [
    "/Users/<user>/Documents/New project 5",
    "/Users/<user>/Documents/New project 4",
    "/Users/<user>/Documents/New project 3"
  ]
}

The folders on disk still existed under the old names:

/Users/<user>/Documents/New project 3
/Users/<user>/Documents/New project 4
/Users/<user>/Documents/New project 5

SQLite threads.cwd also still pointed at the old names:

/Users/<user>/Documents/New project 3  2 threads
/Users/<user>/Documents/New project 4  3 threads
/Users/<user>/Documents/New project 5  9 threads

Rollout JSONL metadata also contained old cwd values:

/Users/<user>/Documents/New project 3  13 session_meta/turn_context cwd records
/Users/<user>/Documents/New project 4  17 session_meta/turn_context cwd records
/Users/<user>/Documents/New project 5  222 session_meta/turn_context cwd records

Relevant public code

The state extraction code treats rollout session_meta and turn_context as thread metadata sources:

codex-rs/state/src/extract.rs

RolloutItem::SessionMeta(_) | RolloutItem::TurnContext(_) => true,
...
if !meta_line.meta.cwd.as_os_str().is_empty() {
    metadata.cwd = meta_line.meta.cwd.clone();
}
...
if metadata.cwd.as_os_str().is_empty() {
    metadata.cwd = turn_ctx.cwd.clone();
}

There is also an explicit regression test asserting that rollout cwd wins even when SQLite has a different cwd:

codex-rs/thread-store/src/local/read_thread.rs

async fn read_thread_preserves_rollout_cwd_when_sqlite_metadata_exists() {
    ...
    let rollout_cwd = PathBuf::from("/");
    ...
    builder.cwd = home.path().join("sqlite-workspace");
    ...
    assert_eq!(thread.cwd, rollout_cwd);
}

That behavior makes a partial rename/remap fragile. Updating only SQLite or only Desktop global state can still leave old rollout cwd values that are read back later.

The current public app-server metadata update API also does not appear to expose a workspace-root/cwd remap operation. ThreadMetadataUpdateParams only accepts git_info:

codex-rs/app-server-protocol/src/protocol/v2/thread.rs

pub struct ThreadMetadataUpdateParams {
    pub thread_id: String,
    pub git_info: Option<ThreadMetadataGitInfoUpdateParams>,
}

The request processor also requires gitInfo and builds a Git-only patch:

codex-rs/app-server/src/request_processors/thread_processor.rs

let ThreadMetadataUpdateParams { thread_id, git_info } = params;
...
let Some(ThreadMetadataGitInfoUpdateParams { sha, branch, origin_url }) = git_info else {
    return Err(invalid_request("gitInfo must include at least one field"));
};
...
let patch = StoreThreadMetadataPatch {
    git_info: Some(StoreGitInfoPatch { ... }),
    ..Default::default()
};

So, from the public code, there does not seem to be a supported app-server operation for "rename/remap this workspace root and all thread cwd metadata under it."

Expected behavior

Project rename should not leave display label and workspace root split.

At minimum, a project rename should do one of these:

  1. Rename only the display label and keep the folder/root label in the UI clearly separate.
  2. Rename/remap the actual workspace root and update all durable path-keyed metadata consistently.

If the Desktop UI presents this as a project rename for an auto-created New project N folder, I would expect option 2.

Suggested fix

Add one supported operation for workspace root rename/remap instead of treating label, root, folder, SQLite metadata, and rollout metadata as separate updates.

Suggested shape:

oldRoot: absolute path
newRoot: absolute path
displayLabel: string
renameFolderOnDisk: boolean

The operation should update, transactionally where possible:

  • Desktop saved workspace roots
  • project order
  • workspace root labels
  • thread workspace-root hints
  • filesystem folder name, if requested and safe
  • SQLite threads.cwd for threads under the old root
  • rollout metadata that can otherwise restore stale cwd

If rewriting old rollout JSONL is not desirable, an append-only canonical remap record or a state DB override that is guaranteed to win over stale rollout session_meta.cwd would also work. The important part is that restart/reload must not recover the old root.

Local repair that fixed the issue

Closing Codex Desktop and applying a complete remap fixed the UI after restart:

/Users/<user>/Documents/New project 3 -> /Users/<user>/Documents/Hermes agent
/Users/<user>/Documents/New project 4 -> /Users/<user>/Documents/Pinchpoint gateway
/Users/<user>/Documents/New project 5 -> /Users/<user>/Documents/Vera Companion app

The repair updated:

~/.codex/.codex-global-state.json
~/.codex/state_5.sqlite threads.cwd
~/.codex/sessions/**/rollout-*.jsonl session_meta.payload.cwd
~/.codex/sessions/**/rollout-*.jsonl turn_context.payload.cwd
the actual folders under ~/Documents

After repair:

rollout files changed: 14
rollout cwd records changed: 252
old SQLite rows for renamed roots: 0

new SQLite rows:
  /Users/<user>/Documents/Hermes agent        2
  /Users/<user>/Documents/Pinchpoint gateway  3
  /Users/<user>/Documents/Vera Companion app  9

No thread content was changed. Only workspace-root metadata was remapped.

Regression test

Suggested regression coverage:

  1. Create three projects with auto-generated folder names under ~/Documents/New project N.
  2. Create at least one thread in each project.
  3. Rename the projects from the Desktop UI.
  4. Restart Desktop.
  5. Assert that the sidebar does not show New project N for renamed projects.
  6. Assert that threads still appear under the renamed projects.
  7. Assert that SQLite threads.cwd and rollout-derived cwd metadata do not restore the old root after restart.

Related issues

This overlaps with existing path/remap reports, but it is not the same repro:

The distinct issue here is that Codex Desktop's own project rename UI can create the mismatch without the user manually moving the folder first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbugSomething isn't workingsessionIssues involving session (thread) management, resuming, forking, naming, archiving

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions