Skip to content

fix(server): Codex rewriting a linked shadow-home file no longer disables the provider - #34900

Open
juliusmarminge wants to merge 4 commits into
mainfrom
factory/issue-26811
Open

juliusmarminge wants to merge 4 commits into
mainfrom
factory/issue-26811

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

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 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.

Before After
6ba3de70af f9b769843a
Before: provider Disabled by cache-file conflict After: provider starts without cache-file conflict
Refresh recording Refresh recording
Server transcript Server transcript

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:

  • 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

AI-assisted; verified with the tests and tools listed above.

…vider usable

Failing repro for #26811.

AI-assisted; verified with the tests and tools listed above.
…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.
@github-actions github-actions Bot added vouch:trusted Author is trusted by repo permissions or the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Nov 12, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at f9b7698

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.

@github-actions

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.5 KiB 21.5 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 1 1 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB 0 B (0.0%) 24.4 KiB ✅
Claude Live turn messages 1 1 0 (0.0%) 8 ✅

Baseline: 0b6f482 · PR result: f9b7698 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@coderabbitai

coderabbitai Bot commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

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.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related issues

Suggested reviewers: t3dotgg, maria-rcks

Merge Risk: Minimal · up to f9b76

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: Passed 4

Check name Status Explanation
Title check Passed 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 Passed 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 Passed 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 Passed 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
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 0b6f482 and f9b7698.

Files selected for processing: 2

  • apps/server/src/provider/Drivers/CodexHomeLayout.test.ts
  • apps/server/src/provider/Drivers/CodexHomeLayout.ts

Limit details: You’ve used all 10 included reviews currently available.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M 30-99 changed lines (additions + deletions). vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Codex shadow home check disables provider when Codex rewrites a linked file (cloud-config-bundle-cache.json)

1 participant