* feat(codex): surface a stalled config sync instead of failing silently
Why: the mirror keeps serving the last synced settings when ~/.codex/config.toml
is missing, blank, or unreadable. That is the right call for data safety, but it
is invisible — a downed WSL distro or an unhydrated cloud-synced home leaves
"Orca ignores my config edits" with no log line and no UI to diagnose.
Status is derived on demand from the same predicates the mirror uses, so the two
cannot disagree. The stall is logged once per episode rather than on every launch
and quota poll, and the Codex account section names the file and what to do.
* fix(codex): latch an unreadable source and stop over-claiming recovery
An unreadable source throws out of the mirror, so reporting only on the success
path left that stall latch-less: it logged the raw failure on every launch and
quota poll while its reason never reached the surfaced status. Report from the
catch path too.
The clear message also claimed the source was "readable again", which is false
when the stall ended because the runtime config was removed rather than because
the source came back.
Restoring console.warn now happens in afterEach — an inline mockRestore is
skipped by a failing assertion, and the leaked spy made every later case in the
block fail spuriously.
* fix(codex): latch the stall promotion hits first, and scope it to the host
Review round 1 findings:
- The unreadable-source latch still never fired in the steady state. Once a
baseline exists, promotion reads the source before the mirror does, so it
throws first and `!promotionPlan` returned before any reporting — logging a
reasonless failure every launch and quota poll, which is exactly what the
previous commit claimed to fix. Report from that branch too. The test only
passed because its fixture had no baseline; it now seeds one first and fails
without the fix.
- The banner named the host's ~/.codex while a WSL or per-account runtime was
selected, whose real source is a different file entirely. Gate it to the host
scope, matching how the sign-in warning is already gated.
- Three new translate keys were missing from the locale catalogs, failing the
localization gate in `pnpm lint`.
- The registrar mock was never asserted, so deleting the registration left the
suite green.
- `codexConfigSyncStatus` hung off the `agentHooks` namespace despite having
nothing to do with agent hooks; moved to its own `codexConfigSync.status`
while it is still a four-file change.
* fix(codex): report sync health for the home the selection actually mirrors
Review round 2:
- The status resolved the shared runtime home, but the system default now runs
Codex directly against ~/.codex and managed accounts get their own home. So a
stalled per-account mirror showed no banner at all, while a stale shared home
could warn about a config the active lane never reads. Resolve the mirrored
home from the current selection, and report synced when the lane has no mirror
to fall behind.
- The round-1 report on the promotion failure path could clear the latch on a
pass where no mirror ran, claiming a recovery that never happened and
silencing every later pass. Only ever latch a stall there; leave clearing to
the path that actually mirrored.
* fix(codex): refetch sync status when the active Codex account changes
Review round 3:
- Resolving the status per selection made the fetch account-dependent, but the
effect was not keyed on the active account. Switching accounts left the banner
describing the previous one — and switching INTO a stalled account showed
nothing at all, which is the silence this change exists to remove.
- Pin the home resolution itself: it had no direct test, and its shared-home
path was a hand-copied literal that could drift from the real helper and
silence the banner with every other test still green.
- Narrow the handler's dependency to the one method it calls, which also drops
an `as unknown as` cast from its test.
- Skip the chmod-based test on Windows, where a read-only directory does not
block writes so the scenario cannot be constructed; matches the convention
already used in config-settings-promotion.test.ts.
* chore(codex): restore the handler docstring and isolate the resolver suite
Round 4 returned clean; these are its two non-blocking nits.
Narrowing the handler param left its JSDoc stranded above the new type, so the
function had no hover doc. The resolver suite also read the developer's real
CODEX_HOME and shell rc, so anyone exporting one would see it fail locally.