fix(codex-native): refresh the probe home's credential, not just its config - #6253
fix(codex-native): refresh the probe home's credential, not just its config#6253dhruv0811 wants to merge 3 commits into
Conversation
…config The persistent probe home is keyed only by the config overrides, so a source home that moves under an unchanged override set left the credential symlink naming the old path while config.toml came from the new one. A removed source was worse: the link dangles, and a dangling link reads as present to the bridge's skip check, so the home could never self-heal and every later probe answered for a logged-out account. Clear every entry the bridge materializes, not just config.toml. Signed-off-by: Dhruv Gupta <dhruv0811@gmail.com>
|
/review |
|
…file lists The refresh loop named config.toml literally while taking the symlink names from a constant. Use _CODEX_HOME_COPY_FILES for the copied half too, so the set stays aligned with what the bridge writes if either list grows. Signed-off-by: Dhruv Gupta <dhruv0811@gmail.com>
|
/review |
|
…obe home The moved-source case was asserted, but the removed-source case was only argued in a comment and checked by hand. That one is the permanent failure mode this fix defends against, so pin it: remove the source home, assert the link actually dangles, then re-probe against a fresh source and require the credential and config to both track it. Signed-off-by: Dhruv Gupta <dhruv0811@gmail.com>
|
/review |
|
|
Resolve-agent reviewed this contributor PR as the candidate fix. Future actionable maintainer review feedback may be remediated automatically. |
There was a problem hiding this comment.
Independent verification of this fix against the live reproduction for OMNI-6010 (mirrors #6252): the reproduction fails before this PR and passes on its head — recommend approval.
Fail→pass evidence (repro e2e test driving the real probe_codex_model_options → codex app-server 0.139.0 boot under an isolated $HOME):
- On unfixed
main(ac99ec2): aftermv codex-a codex-b+CODEX_HOMEswitch with byte-identical overrides (single cache keye3b0c44298fc), the probe home'sauth.jsonis a dangling symlink to the oldcodex-a/auth.json(exists()False,is_symlink()True) — every later probe boots codex logged out, silently, forever. - On this PR's head (f6b0763): the same journey passes —
auth.jsonexists and resolves into the movedcodex-bhome. - The second facet (source home removed and recreated at a new path — the permanent case, since a dangling link satisfies the bridge's
exists() or is_symlink()skip) was verified independently the same way: dangles forever on unfixed code, self-heals on this head. The PR's own unit-test extension catches both regressions (confirmed failing against the pre-fix module).
Diff review: the approach is the right seam. Rather than reintroducing hand-rolled per-file relinking, it reuses the bridge's own _CODEX_HOME_SYMLINK_FILES / _CODEX_HOME_COPY_FILES constants as the source of truth for what to clear, so the clear set cannot drift from the populate set, and it restores the unconditional-refresh property the pre-#6249 code had. Each unlink is suppressed/missing_ok, so partially-populated homes are safe. Full tests/test_codex_native_app_server.py passes on this head (76 passed), and the touched tests also pass under a hostile ambient env (CODEX_HOME/OPENAI_API_KEY/DATABRICKS_HOST exported).
Polly: a real review exists for this exact head — no blocking/security findings. Its three non-blocking notes were re-assessed: (1) memories_1.sqlite re-linked per probe is harmless and more correct for a moved source; (2) dir symlinks are correctly out of scope — probes run minimal_config=True, and the dir loop is gated on if not minimal_config; (3) the unlink→repopulate race is pre-existing (already true of config.toml alone) and not introduced here.
CI: all checks green on f6b0763; branch is MERGEABLE/CLEAN against main.
Validate the fix live (this fix runs in the runner/host process, so check out the PR — a deployed preview's local runner would run unfixed code):
gh pr checkout 6253
# with the codex CLI on PATH, in an isolated HOME:
# seed $HOME/codex-a with auth.json + config.toml, export CODEX_HOME=$HOME/codex-a, probe once:
python -c 'import asyncio; from omnigent.codex_native_app_server import probe_codex_model_options as p; print(len(asyncio.run(p())))'
# mv $HOME/codex-a $HOME/codex-b; export CODEX_HOME=$HOME/codex-b; probe again, then check:
# $HOME/.omnigent/cache/codex-model-probe/<key>/auth.json must exist and resolve into codex-b
This is an automated reviewer's verification; a maintainer's approval is still required to merge.
Related issue
Closes #6252
Summary
Follow-up to #6249, addressing a review note on it. That PR consolidated the
model-probe home onto the shared
_populate_codex_home_configbridge, which isthe right call, but the bridge skips any entry that already exists and the probe
home is a persistent cache keyed only on the
-coverrides.config.tomlwasunlinked before the bridge ran; the credential symlink was not.
config.toml.key never changes: a source home that moves leaves the probe serving
config.tomlfrom the new source with the credential still naming the old one;a source home that is removed leaves the link dangling. The second is
permanent, because a dangling symlink returns
Falsefromexists()butTruefromis_symlink(), and the bridge's skip test isif link_path.exists() or link_path.is_symlink(). So the home neverself-heals and every later probe answers for a logged-out account (login-gated
entries missing, wrong account default) with no error to point at.
auth.jsonsymlink unconditionally on each call. Consolidating onto the bridgetraded that for skip-if-present, and this puts it back for the whole bridged
set rather than one file.
Test Plan
3 passed. The new assertions are confirmed to catch the regression: narrowing the
refresh back to
config.tomlalone fails with the credential still resolving intothe old source home while the config came from the moved one:
The removed-source case is covered too, and asserted independently of the
moved-source case (isolating it against a narrowed fix fails with
AssertionError: a dangling credential link must self-heal). It removes thesource home, requires the link to actually dangle, then re-probes against a
fresh source and requires both the credential and the config to track it.
The dangling-symlink mechanism this all turns on was verified directly rather
than assumed:
Live, against a real
~/.codex/config.tomlon a Databricks gateway provider,cache cleared first, confirming the added unlink does not break the warm-cache
path it runs on every probe:
with the credential correctly relinked afterwards:
Demo
Type of change
Test coverage
Coverage notes
The unit test extends the existing probe-home test with both failure modes. The
moved-source case renames the source home, repoints
CODEX_HOME, re-probes underthe same overrides, and asserts the credential resolves into the new source. The
removed-source case then deletes it, asserts the link actually dangles (this is
what the bridge silently accepts), and requires a re-probe against a fresh source
to self-heal. Both assertions were checked to fail against a fix narrowed back to
config.tomlalone, the second one in isolation so it is not merely riding onthe first.
Manual verification covers the warm-cache path against the real
codexbinary.The added unlink runs on every probe including cache hits, so the thing worth
checking by hand is that a second probe against an unchanged source still returns
the full catalog rather than paying a re-link that breaks it.
Changelog
The codex model picker no longer falls back to a logged-out model list after your Codex config home moves or is recreated.
Issues
Resolves OMNI-6010