Repository navigation
fix(codex): register Desktop projects and bind managed threads - #1442
Merged
Merged
Conversation
Codex Desktop default thread/list excludes exec-origin threads even when their rollout and index rows exist. Route ordinary managed Codex turns through the existing native app-server path, independently of context recovery, so fresh sessions acquire an interactive source. Preserve extra workspace roots through native configuration. Cover transport selection and writable roots with unit tests, and add an opt-in real Codex protocol test using isolated account/catalog homes and a local Responses fixture. Verify fresh and resumed default-list visibility with a stable id after process restart. CLI suite: 457 passed; native protocol test passed. Existing exec-origin histories are not rewritten. Pre-commit hook ran. Total eslint: 0, total circular: 1
Separate the project workspace from account and Desktop index directories in the real native protocol fixture. After fresh and resumed turns, assert the target project lists the same thread exactly once while an unrelated project and both storage directories list nothing. The isolated real Codex binary test passed using local Responses fixtures; no production conversations were modified. Pre-commit hook ran. Total eslint: 0, total circular: 1
Resolve the session repository against the native project registry before thread creation. Reuse existing projects without renaming them, create missing projects with an idempotent native API request, and pass the real projectId to thread/start. Keep repository identity separate from worktree execution and retain project assignment on resume. Project discovery runs on a blocking worker with bounded pagination and request deadlines. The real native fixture verifies project records, reuse, projectId membership, unrelated-project exclusion, and stable resumed identity while executing outside the project root. Native fixture and 457 CLI tests pass. Desktop UI automation remains prohibited by the tool. Pre-commit hook ran. Total eslint: 0, total circular: 1
Neonforge98
marked this pull request as draft
September 9, 2026 05:35
Neonforge98
marked this pull request as ready for review
September 9, 2026 08:01
This was referenced Sep 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
New ORG2 Codex conversations are persisted and readable by ID but absent from Codex Desktop's sidebar/search catalog. Ordinary managed turns use
codex exec, producingsource=exec; the Desktop defaultthread/listexcludes that source. Project metadata is not the cause: the affected records have the same cwd/project fields as visible conversations. Prior native-continuation work only enabled app-server for recovery episodes, leaving normal creation on exec. A directory-only repair also does not register a missing Desktop project or explicitly bind worktree-executed sessions to their repository project.Solution
Route ordinary managed Codex turns through the existing app-server transport. Before a fresh thread is created, use the native
project/listAPI to find the Desktop project whose root matches the ORG2 session's repository. Reuse that project without changing its name or metadata; if absent, register it throughproject/createwith a stable path-derived idempotency key. Supply the returnedprojectIdtothread/start. Registration uses the repository root even when execution happens in a separate worktree. Resumes preserve the existing native project assignment.Reuse account-scoped authentication, Desktop binary/index selection, permission/model handling, developer instructions, and bounded turn cleanup. Preserve extra workspace roots through native config. No private database writes or UI filtering are used.
The opt-in real Codex protocol test now registers and lists actual native project objects, verifies reuse without duplicates, and creates a thread executing outside the project root. After fresh and resumed turns in separate processes, querying by the target
projectIdmust return the same thread once; the unrelated project must have no members. The test also checks project name/root metadata and literal user text using local Responses fixtures and isolated account/index/workspace directories.Potential risks
Codex App catalog/sidebar refresh is owned by the App (periodic state-DB reconcile plus focus refetch, see the discovery section below); a fresh thread can take a few minutes to appear in a running App, and ORG2 cannot force it. Initial App listings reported
projectId: nullfor fresh Astra threads. A later recheck of a fresh, still-unopened thread reported the correct existing sidebar project before any native App continuation. This corrects the earlier inference that continuation was required. The App's explicit thread/project assignment table had no entry for that thread; cwd-based sidebar grouping can supply the assignment after discovery. Automatic registration of an App-side project that does not already exist remains unresolved.This changes the transport used by ordinary Codex sessions and selects the Desktop-compatible binary through the existing native path. Custom exec-only argument overrides do not apply to native JSON-RPC turns. Automatic project registration requires a Desktop binary supporting the experimental projects API; an unsupported/failed registration is surfaced rather than pretending project placement succeeded. A new workspace creates a durable native project item. An interrupted or failed first turn can leave an empty project; retries reuse it. Existing Desktop project names and metadata are preserved. Hosted/custom gateways, interactive approval flows, images, cancellation, and full rendered Desktop refresh were not exercised end to end in this PR; existing unit coverage runs, but the live fixture uses local HTTP responses without tools.
Existing exec-origin conversations are not relabeled or rewritten; resuming them does not promise to change their native source. Historical migration is separate from preventing new invisible threads. No database/schema migration, credential migration, dependency change, or private Codex SQLite write is introduced; project records are created through the native API. Revert this commit and rebuild to roll back the routing change; already-created native threads and projects remain in their store and can be managed through Desktop normally. The local application bundle was rebuilt from this head and launched for the live checks below.
Verification
cargo test --manifest-path src-tauri/Cargo.toml --lib agent_sessions::cli::— 457 passed, 4 opt-in tests ignored, 0 failures.src-tauri:ORGII_NATIVE_CODEX_APP_BINARY=/Applications/ChatGPT.app/Contents/Resources/codex cargo test --lib live_native_fresh_and_resumed_turns_are_in_default_desktop_list -- --ignored --nocapture— passed with the installed Desktop binary, two local Responses calls, separate account/index/project/worktree directories, native project creation/reuse, fresh/resume turns, and process restart. Actualproject/listrecords are checked for names/roots and uniqueness.thread/listfiltered by the real targetprojectIdreturns the worktree-executed thread once after each turn; an unrelated project has no members. No real account requests or production data writes.rustfmt --edition 2021 --check src-tauri/src/agent_sessions/cli/parsers/tests/codex_app_server_tests.rs src-tauri/src/agent_sessions/cli/tests/runner_command_tests.rs— passed.cd src-tauri && cargo clippy --lib --message-format=short -p org2— passed; hooks ran without bypass.git diff --check— passed; inspected the diff for secrets, private data, generated artifacts, and unrelated changes.origin/develop; base remained current before commit (git rev-list --left-right --count HEAD...origin/developreturned 0/0).Live acceptance follow-up
77c97a290efa14dc8f68627edee45868930add05;pnpm run tauri:build:fastpassed, and the resulting macOS bundle was launched. Codex bundled CLI 0.153.4 and Claude Code 2.1.263 were checked.claude-fable-5-1and the expected repository cwd. ORG2 initially displayed “No activity yet” after first completion; Reload recovered the actual history. This is an open hydration defect, not a passing initial-render result.agent_sessions::cli::session_runner::command_tests: 42 passed.Architecture and lifecycle review
Reviewed the changed call chain across the ten architecture layers: compilation, shared transport ownership, naming/comments, separation of recovery from transport, Codex-only defaults, domain boundaries, readability, serialized protocol, fresh/resume initialization, and unchanged account/model/cwd resolution. The source and project-membership invariants are owned by native project/thread creation rather than UI filtering or direct metadata repair. Repository identity is separated from the worktree execution directory. Broad unrelated frontend/type refactors are outside this bug fix.
Performance verdict: blocked for full rendered lifecycle acceptance because Tauri hidden/idle/cancel and multi-account measurements were not run. The protocol/listability checks above passed; this PR makes no measured performance-improvement claim.
Desktop UI automation was attempted but the Computer Use tool prohibits access to Codex Desktop. Project registration and membership were verified through native APIs; actual sidebar rendering and clicking to resume remain unverified.
App discovery boundary, resolved by live check (2026-09-09 00:34–00:45 local)
Read-only inspection of the installed App and a live run against it identify the actual mechanism, replacing the earlier "unresolved runtime handoff" note:
codex app-server(pid 97737, stdio) and a private catalog (thread_history_1.sqlite, tablethread_history_projection_state). That server reconciles the shared state DB (state_5.sqlite, where this PR'sproject/createandthread/startwrite) into its catalog on its own cadence. A fresh ORG2 thread created at 00:34:10 entered the App catalog at 00:37:22 with no App interaction. The renderer's sidebar/search are query caches that refetch on window focus and invalidate onthread/startedfrom the App's own connection, so an external process cannot push the update; it appears after the App's next reconcile and focus refetch.77c97a290(bundle/private/tmp/ORG2-native-integration-final.app): ORG2 createdAPPSYNC_0909_T1(Codex, GPT-6 Astra Medium, repository ORGII). State DB row: sourcevscode,project_id 01a048d0…= the App's existing ORGII project, cwd/Users/vinceorz/Projects/ORGII. Before the App regained focus its ORGII section did not list the thread; after focus plus one reconcile the App search returned it taggedORGII, and the sidebar listed it under ORGII. Opening it in the App rendered T1; a continuation sent from the App quoted the T1 marker and answeredAPPSYNC_0909_T2_OKin the same UUID, still under ORGII. The App appended to the same rollout file that ORG2's account profile links to.Shared-runtime feasibility check (2026-09-09)
Ran
node /tmp/orgii-shared-runtime-probe.mjsagainst the installed native Codex binary using a fresh temporary CODEX_HOME and repository, a loopback WebSocket listener, and two initialized protocol clients. The creator called project/create and thread/start. The observer received both project/changed and thread/started. No real user prompt/model request, production database modification or App restart occurred. This establishes cross-client notification delivery within one app-server, not live Desktop project registration.The installed App contains a local-daemon transport branch, but it requires an empty config-override list. Its current local launcher supplies a codex_app MCP override even in the missing-plugin branch, so setting CODEX_APP_SERVER_USE_LOCAL_DAEMON alone does not establish that connection. A separate CODEX_APP_SERVER_WS_URL transport branch exists. Switching an existing App to it would alter startup/runtime ownership and requires separate compatibility validation; it was not enabled in the user's running App. App sidebar project registration also remains separate from receiving native project/changed notifications.