You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Update recovery can replace a managed service with an unsupervised detached proxy #7043
If background-service repair fails or is judged non-viable after ocx update, the updater falls back to a detached proxy (src/update/index.ts:554-579, invoked from :904-959) and treats a healthy endpoint as full recovery. The process is no longer supervised by launchd/systemd: a crash is not restarted, and the next launchctl kickstart races with the detached process for the port.
Reproduction
From the 2.84.0 source path, with an isolated install or mocked service backend:
Start with an installed, managed service.
Run update recovery with service repair failing and the listen port free.
Let the direct-start fallback pass its identity/health probe.
Compare the serving PID with the PID registered in the launchd job.
We did not break our live hub to reproduce this; we guard against it in a wrapper by checking that the launchd job PID equals ~/.opencodex/ocx.pid after every update and restarting through launchd otherwise.
Version
@bitkyc08/opencodex 2.84.0
Operating system
macOS 27 (launchd); Linux/systemd follows the same code path.
Provider and model
n/a
Logs or error output
The fallback deletes OCX_SERVICE, spawns with detached: true and unref(), then only verifies serving identity.
Redacted configuration
Hub mode, LaunchAgent com.opencodex.proxy.
Proposed fix
For an originally managed service prefer manager-mediated recovery (launchctl kickstart -k / systemctl --user restart). If a detached emergency start stays intentional, return an explicit degraded-supervision result with a non-zero exit code instead of reporting recovery as complete, and add a regression that checks the manager PID against the serving PID.
Fixed on dev by #7074 (b54a21c). After a failed or non-viable post-update service refresh, both ocx update lanes first ask launchd (kickstart -k) or systemd (systemctl --user restart) to restart the job. Recovery counts only when the serving PID is the manager's main PID or its direct child. Otherwise the detached fallback runs with an UNSUPERVISED warning and the update exits non-zero. A manager restart that is still pending starts no competing proxy. The dashboard update worker (src/update/job.ts) keeps its own fallthrough for now.
Activity
fix(update): settle timed-out manager restarts, honor vetoes, ignore …
fix(update): recover through launchd/systemd before an unsupervised f…
lidge-jun commentedon Oct 13, 2026
Fixed on dev by #7074 (b54a21c). After a failed or non-viable post-update service refresh, both
ocx updatelanes first ask launchd (kickstart -k) or systemd (systemctl --user restart) to restart the job. Recovery counts only when the serving PID is the manager's main PID or its direct child. Otherwise the detached fallback runs with an UNSUPERVISED warning and the update exits non-zero. A manager restart that is still pending starts no competing proxy. The dashboard update worker (src/update/job.ts) keeps its own fallthrough for now.fix(update): dashboard update restarts through launchd/systemd before…