You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(server): Codex rewriting a linked shadow-home file no longer disables the provider - #34900
The review item is right: config.toml still reached the conflict rejection. The previous round's review rejected copying a shadow config.toml into the shared home, because a differing shadow config.toml may be the user's per-account config. So ensureSymlink in apps/server/src/provider/Drivers/CodexHomeLayout.ts now handles a regular-file config.toml without writing to the shared home. If its bytes match the shared config.toml (Codex re-saved it by write-temp-then-rename), the file is replaced with the link again. If it differs, it is kept as that account's own config, a warning is logged, and setup continues. In both cases the provider is no longer disabled and the shared config.toml is never modified. cloud-config-bundle-cache.json, cloud-requirements-cache.json, .codex-global-state.json and its .bak stay private to each shadow home, as in the previous round. Any other shared entry that is a regular file in the shadow home still raises CodexShadowHomeEntryConflictError. Tests: the repro test also replaces the linked config.toml by rename with identical bytes and asserts the link is restored; a new test asserts a differing shadow config.toml is kept and the shared one is unchanged; the existing rejection test now uses history.jsonl. Against the previous head 97442e8 the two config.toml cases fail (2 failed, 9 passed); with this change all 11 pass. No regex or parser was added.
On origin/main 6ba3de7, in a fresh worktree with T3CODE_DEV_SEED=0: created a shared Codex home containing config.toml and cloud-config-bundle-cache.json, and set the worktree settings.json providerInstances.codex_work = {driver: codex, config: {homePath: , shadowHomePath: /work}}. Ran vp run dev: the shadow home was materialized with cloud-config-bundle-cache.json and config.toml as symlinks to the shared home, and Codex ran there (it created its own sqlite files). Then replaced the cache the way Codex does (wrote .cloud-config-bundle-cache.json.tmp and renamed it over cloud-config-bundle-cache.json), so the link became a regular file. Stopped and restarted the dev server: the server logged ERROR Failed to create provider instance {instanceId: 'codex_work', driver: 'codex', detail: "Cannot create Codex shadow home entry 'cloud-config-bundle-cache.json' because '<parent>/work/cloud-config-bundle-cache.json' already exists and is not a symlink."}, which disables the instance. Also added a unit test to CodexHomeLayout.test.ts that does the same for cloud-config-bundle-cache.json and .codex-global-state.json (the commenter's file): it fails on main, and the other 9 tests in the file pass.
Evidence
Create a shared Codex home with config.toml and a synthetic cloud-config-bundle-cache.json, configure codex_work with a shadow home, and start the real dev server. Atomically replace the shadow cache via temporary-file rename, restart, then open Settings > Providers and refresh the matching instance. Both sides use fresh synthetic state, T3CODE_DEV_SEED=0, Chromium at 1280×800, and the same light theme. Captures are cropped; local paths are redacted.
Base reproduces the exact non-symlink startup conflict. Head preserves the account-local regular cache and creates the provider successfully. No credentials were seeded, so head reports Not authenticated; an authenticated agent turn was not verified.
AI-assisted; verified with the tests and tools listed above.
Not shown:
An authenticated Codex turn and Codex's hourly cache rewrite were not exercised; the reported temporary-file rename was reproduced directly, without account credentials.
Other private cache filenames, .codex-global-state.json, and config.toml reconciliation were not exercised; this proof targets cloud-config-bundle-cache.json.
Tests
vp test run src/provider/Drivers/CodexHomeLayout.test.ts (apps/server): 11 passed; the same tests with the 97442e8 source: 2 failed, 9 passed. vp run check:changed: format, lint, knip, graphql, typecheck (3 packages), tests (2 files) all pass.
Not in this change
cloud-config-bundle-cache.json and cloud-requirements-cache.json are per-ChatGPT-identity caches and arguably belong in PRIVATE_ENTRY_NAMES (as closed PR fix(server): Codex shadow homes keep their own cloud config bundle cache #26817 proposed). With this fix they stay shared and work like a working link, where the last writer wins.
The shared file is overwritten in place, not atomically, when the shadow copy is newer. A process reading the shared file at that moment could briefly see a partial file.
Whether .codex-global-state.json should eventually be shared via a reconcile step instead of being kept per shadow home is left to the maintainers.
A shadow home whose config.toml differs from the shared one stops receiving later edits to the shared config.toml; the only signal is the logged warning.
…bles the provider
Codex saves files such as cloud-config-bundle-cache.json and
.codex-global-state.json by write-temp-then-rename, which turns the shadow
home's link into a regular file. The next shadow-home setup rejected that
file with CodexShadowHomeEntryConflictError and the instance was disabled.
When the shadow entry and the shared entry are both plain files, setup now
does what a working link would have done: the newer copy becomes the shared
one, the displaced copy is kept in the shadow home's .t3-shadow-conflicts
folder, and the link is restored. SQLite files, lock files and directories
still raise the conflict error.
Fixes#26811
AI-assisted; verified with the tests and tools listed above.
The previous change copied whichever real file in a shadow home was newer
into the shared Codex home. A real config.toml in a shadow home is the
user's own (Codex writes config.toml through the link), so that could
overwrite the main config. Drop that reconciliation and keep the conflict
error for config.toml.
Instead, cloud-config-bundle-cache.json and cloud-requirements-cache.json
(signed per ChatGPT identity) and .codex-global-state.json with its .bak
(saved by write-temp-then-rename) are private to each shadow home, like
models_cache.json. An existing link to the shared copy is removed, and a
real file Codex left there is kept.
Fixes#26811
AI-assisted; verified with the tests and tools listed above.
…bles the provider
When the shadow home's config.toml is a regular file, setup no longer fails.
If it matches the shared config byte for byte (Codex re-saved it by rename),
the link is restored. If it differs, it may be that account's own config, so
it is kept and the shared config.toml is not touched. Other shared entries
that are regular files still raise the conflict error.
Fixes#26811
AI-assisted; verified with the tests and tools listed above.
Macroscope's review found this PR approvable — This prevents Codex file rewrites from disabling configured shadow-home providers while preserving account-local configuration. Shared file contents and default direct-home behavior remain unchanged, limiting the impact.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review.
Walkthrough
Codex shadow-home materialization now excludes additional cloud-cache and global-state files from shared entries. For an existing regular config.toml, it compares the shadow and shared contents, keeps differing shadow content with a warning, and continues linking identical content. Tests cover atomic rewrites, preserved files, restored links, and conflicts for history.jsonl.
Changes: Codex shadow-home materialization
Layer / File(s)
Summary
Shadow-home entry recovery and validation apps/server/src/provider/Drivers/CodexHomeLayout.ts, apps/server/src/provider/Drivers/CodexHomeLayout.test.ts
The private-entry set adds cloud-cache and global-state files. For a regular shadow config.toml, materialization reads and compares both files, retains differing shadow content with a warning, and continues the existing replacement flow for matching content. Read failures use the readFile operation. Tests cover atomic rewrites, retained shadow configs, restored links, and conflicts for history.jsonl.
The change stops a rewritten linked config.toml from disabling the Codex provider. It keeps a differing shadow copy with a warning and restores the link when contents match. Only a narrow, self-limited race on config writes remains, so it is reasonable to merge.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title names the main change: Codex rewriting a linked shadow-home file no longer disables the Codex provider. The changeset adds handling for a regular-file config.toml in shadow homes (restore th…
Linked Issues check
Directly linked issue #26811 has two coding requirements: (1) do not disable the provider when Codex replaces a shadow-home link with a regular file, and (2) make cloud-config-bundle-cache.json privat…
Out of Scope Changes check
All changes in the whole-PR diff (mergeBase 6ba3de7 to head f9b7698) are in apps/server/src/provider/Drivers/CodexHomeLayout.ts and its test file. They serve the linked issue #26811. The PRIVATE…
Description check
The description covers the problem (Codex rewriting a linked shadow-home config.toml causes CodexShadowHomeEntryConflictError and disables the provider), the change (ensureSymlink compares a regular-f…
Full details: Title check
Explanation
The title names the main change: Codex rewriting a linked shadow-home file no longer disables the Codex provider. The changeset adds handling for a regular-file config.toml in shadow homes (restore the link when bytes match, keep and warn when they differ) and private cache entries, which the title covers at the right level of detail. The title is a conventional commit with a clear scope and summary, though at 86 characters it is slightly long.
Full details: Linked Issues check
Explanation
Directly linked issue #26811 has two coding requirements: (1) do not disable the provider when Codex replaces a shadow-home link with a regular file, and (2) make cloud-config-bundle-cache.json private, and handle other shared entries such as config.toml with a warning or by relinking when contents match. At head f9b7698, CodexHomeLayout.ts meets both. (1) PRIVATE_ENTRY_NAMES now includes cloud-config-bundle-cache.json, cloud-requirements-cache.json, .codex-global-state.json and its .bak, so these are excluded from linking and removed as stale symlinks by removePrivateSymlink. (2) For a regular-file config.toml, ensureSymlink reads the shadow and shared copies. Identical contents fall through to the existing remove-and-link path, which restores the link. Differing contents return a logWarning and leave the shared file untouched. Other regular-file shared entries still raise CodexShadowHomeEntryConflictError, as the issue does not request otherwise. The PR's test changes cover the rename-replacement and differing-config cases. Note: the PR objectives text says the cloud cache files remain shared, and the PR description's 'Not in this change' section says the same. The code and the PR description's main paragraph show they are private. This is a documentation inconsistency. The code matches the issue's requested behavior.
Full details: Out of Scope Changes check
Explanation
All changes in the whole-PR diff (mergeBase 6ba3de7 to head f9b7698) are in apps/server/src/provider/Drivers/CodexHomeLayout.ts and its test file. They serve the linked issue #26811. The PRIVATE_ENTRY_NAMES additions (cloud-requirements-cache.json, .codex-global-state.json, .bak) apply the same rule as the requested cloud-config-bundle-cache.json change, because Codex also replaces these files by rename. The readFile operation literal supports the new config.toml comparison. The config.toml branch is limited to the issue's requested behavior. No unrelated behavior change was found. The out-of-scope judgment relies on the diff summary and the diff of CodexHomeLayout.ts; test file content was not separately inspected.
Full details: Description check
Explanation
The description covers the problem (Codex rewriting a linked shadow-home config.toml causes CodexShadowHomeEntryConflictError and disables the provider), the change (ensureSymlink compares a regular-file config.toml with the shared one: identical bytes restore the link, differing bytes are kept with a warning), scope (links the issue #26811 and states it is a bug fix), and verification (unit test results before and after, a real-app reproduction on main with evidence, and the list of checks run). It also lists untested areas and known limitations. Minor gaps: the Scope section does not explain why the change is limited to a bug fix, the template's note about agent model and harness is not clearly addressed (it says AI-assisted but does not name the model and harness), and the description is long and repeats some items (the 'Other regular files' limitation appears twice, and the AI-assisted line appears twice). These gaps are non-critical, so the check passes.
Finishing Touches
Generate unit tests (beta)
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Limit details: You’ve used all 10 included reviews currently available.
This branch has not been deployed
No deployments
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
size:M30-99 changed lines (additions + deletions).vouch:trustedAuthor is trusted by repo permissions or the VOUCHED list.
1 participant
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.
The review item is right: config.toml still reached the conflict rejection. The previous round's review rejected copying a shadow config.toml into the shared home, because a differing shadow config.toml may be the user's per-account config. So ensureSymlink in apps/server/src/provider/Drivers/CodexHomeLayout.ts now handles a regular-file config.toml without writing to the shared home. If its bytes match the shared config.toml (Codex re-saved it by write-temp-then-rename), the file is replaced with the link again. If it differs, it is kept as that account's own config, a warning is logged, and setup continues. In both cases the provider is no longer disabled and the shared config.toml is never modified. cloud-config-bundle-cache.json, cloud-requirements-cache.json, .codex-global-state.json and its .bak stay private to each shadow home, as in the previous round. Any other shared entry that is a regular file in the shadow home still raises CodexShadowHomeEntryConflictError. Tests: the repro test also replaces the linked config.toml by rename with identical bytes and asserts the link is restored; a new test asserts a differing shadow config.toml is kept and the shared one is unchanged; the existing rejection test now uses history.jsonl. Against the previous head 97442e8 the two config.toml cases fail (2 failed, 9 passed); with this change all 11 pass. No regex or parser was added.
Fixes #26811
Reproduction on main
On origin/main 6ba3de7, in a fresh worktree with T3CODE_DEV_SEED=0: created a shared Codex home containing config.toml and cloud-config-bundle-cache.json, and set the worktree settings.json providerInstances.codex_work = {driver: codex, config: {homePath: , shadowHomePath: /work}}. Ran
vp run dev: the shadow home was materialized with cloud-config-bundle-cache.json and config.toml as symlinks to the shared home, and Codex ran there (it created its own sqlite files). Then replaced the cache the way Codex does (wrote .cloud-config-bundle-cache.json.tmp and renamed it over cloud-config-bundle-cache.json), so the link became a regular file. Stopped and restarted the dev server: the server loggedERROR Failed to create provider instance {instanceId: 'codex_work', driver: 'codex', detail: "Cannot create Codex shadow home entry 'cloud-config-bundle-cache.json' because '<parent>/work/cloud-config-bundle-cache.json' already exists and is not a symlink."}, which disables the instance. Also added a unit test to CodexHomeLayout.test.ts that does the same for cloud-config-bundle-cache.json and .codex-global-state.json (the commenter's file): it fails on main, and the other 9 tests in the file pass.Evidence
Create a shared Codex home with config.toml and a synthetic cloud-config-bundle-cache.json, configure codex_work with a shadow home, and start the real dev server. Atomically replace the shadow cache via temporary-file rename, restart, then open Settings > Providers and refresh the matching instance. Both sides use fresh synthetic state, T3CODE_DEV_SEED=0, Chromium at 1280×800, and the same light theme. Captures are cropped; local paths are redacted.
Reproduction driver.
Base reproduces the exact non-symlink startup conflict. Head preserves the account-local regular cache and creates the provider successfully. No credentials were seeded, so head reports Not authenticated; an authenticated agent turn was not verified.
AI-assisted; verified with the tests and tools listed above.
Not shown:
Tests
vp test run src/provider/Drivers/CodexHomeLayout.test.ts(apps/server): 11 passed; the same tests with the 97442e8 source: 2 failed, 9 passed.vp run check:changed: format, lint, knip, graphql, typecheck (3 packages), tests (2 files) all pass.Not in this change
AI-assisted; verified with the tests and tools listed above.