Triage
Confirmed on v0.0.44 (451afcb). Nothing after that commit touches this path, and there is no open PR.
Managed ChatGPT instances use an auth-overlay shadow home (CodexManagedHome.ts: any instance id other than codex gets providers/codex/<id>/shadow). materializeCodexShadowHome then symlinks every non-private entry of ~/.codex into that directory, including the known directories it creates itself (sessions, sqlite, skills, and the rest). ensureSymlink calls Effect's FileSystem.symlink, which has no Windows link-type argument and ends up as fs.symlink with no type. On Windows that is a real symlink, so a normal user without Developer Mode gets EPERM. The status check catches that and replaces it with "Could not check Codex right now," which matches the trace (sessions → refreshSnapshot). The built-in CLI provider (instanceId === "codex") stays in direct mode and does not hit this.
Developer Mode only has to stay on because the entry list is rebuilt from readDirectory on every refresh. A new file such as ..codex-global-state.json.bak.tmp-* is not in PRIVATE_ENTRY_NAMES or SHADOW_LOCAL_ENTRY_NAMES, so one unprivileged create fails the whole provider check. #14321 is the same sign-in flow, but it is the model catalog, not this.
Junctions, as in antigravityAuthSupport.ts, cover directories only, and they have to go through node:fs because Effect cannot pass "junction". Files still need a link, and a hard link or copy is not a symlink: the next refresh's readLink returns EINVAL, readLinkState treats that as NotSymlink, and config.toml (anything outside mcp-oauth-locks) fails with CodexShadowHomeEntryConflictError. Skipping transient *.tmp-* names, or not failing the check when an unknown entry cannot be linked, is still required for the recurrence.