Skip to content

Windows: a second OpenCodex home can never pass admitCodexWrite once Task Scheduler holds opencodex-proxy #2800

Description

@adtumk

Client or integration

CLI

Area

Codex sync / service ownership

Summary

On a machine where the opencodex-proxy scheduled task is registered, any second OpenCodex
home is permanently refused write admission — not blocked by a race or a stale lock, but by the
ownership model itself. ocx sync can never complete from that home, so its Codex config is
never injected and its proxy never reaches ready.

Scheduler registration is machine-global. The task-definition XML that proves ownership is
per-home. A second home therefore sees "the scheduler holds the task" and "my task XML is absent"
simultaneously, which the probe reports as unproven ownership.

Reproduction

  1. On a machine where OpenCodex is installed normally and the opencodex-proxy scheduled task is
    registered from home A.
  2. Create an unrelated home B (HOME / USERPROFILE / CODEX_HOME pointing at a fresh
    directory) with a minimal .opencodex/config.json.
  3. Start a proxy from home B on a free port. It starts and /healthz is ok.
  4. Run ocx sync from home B.

Actual

ocx sync prints:

Codex sync did not complete. Fix the reported Codex config issue and retry.

…and exits 1. Nothing is written. Calling the admission gate directly shows the real reason:

{
  "kind": "refused",
  "authority": "service-home",
  "message": "Refusing to write because ownership could not be proven: Task Scheduler holds opencodex-proxy but its task XML is missing."
}

/readyz on the home-B proxy stays failed indefinitely, because readiness is gated on the
post-startup sync that just refused. /healthz stays ok, so the proxy looks healthy while
nothing it needs to inject has been injected.

Expected

Either:

  • ownership is evaluated per home, so a home that does not claim the scheduled task is not judged
    against another home's registration; or
  • the refusal is explicit and actionable — it names the owning home, and ocx sync surfaces the
    reason and exits non-zero.

Why this is hard to diagnose

The admission gate produces a precise message and the CLI discards it, so the operator sees an
instruction ("fix the reported Codex config issue") referring to a report that was never printed.
The process does exit non-zero, but the actionable refusal reason is still hidden. #1461 fixed the
injection-preflight diagnostic, but this refusal comes from the earlier machine-global service-home
admission gate, whose message still never reaches the operator.

Impact

Any multi-home use of the CLI on one machine: staging or a second profile alongside a normal
install, CI on a developer machine, or testing a candidate build without disturbing the working
one. There is no configuration that avoids it, because the collision is between a machine-global
registration and a per-home proof.

Notes

Found while validating an unrelated change in an isolated second home; the workaround there was
the code's own CodexSyncDeps.admitCodexWrite test seam, which is not available to operators.

Correction after a clean re-run: an earlier version of this report said ocx sync exited 0. The
real CLI exits 1. The earlier 0 came from reading the status of a later command in a shell pipeline,
not the ocx sync process itself. The ownership refusal and swallowed diagnostic reproduce
unchanged.

Version

Reproduced again against upstream dev @ 80e251a4db5ab600ef0a0b44c9d24b326c37d40b (2.35.0),
built from source in an isolated dev/staging environment (isolated home, isolated port). This is
after #2815; that Windows sync fix does not change this service-home admission path, and the issue
still reproduces.

Operating system

Microsoft Windows 11 Pro, version 25H2, build 26200.9168, 64-bit.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingplatformOS/service/tray/ACL (Windows-heavy, not Windows-only)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions