Area
Service lifecycle
What are you trying to accomplish?
Separate live proxy process-state ownership from persisted configuration ownership without changing startup, shutdown, status, service, update, or stale-state cleanup behavior.
What prevents this today?
src/config.ts owns both persisted config loading/writes and the live process records used by the proxy: ocx.pid, runtime-port.json, PID parsing, liveness checks, command-line identity verification, and snapshot-guarded cleanup. Internal callers that need only process state therefore depend on the broad config persistence module, and lifecycle correctness tests remain mixed into config tests.
The ownership is also split across distant parts of the file: path helpers and PID/runtime-port I/O are not one contiguous block. A mechanical extraction must therefore be guided by symbols and tests rather than line ranges.
What should OpenCodex do?
- Introduce one leaf module for proxy process state under the existing config namespace, without creating a second runtime implementation.
- Move the PID/runtime-port path helpers,
RuntimePortState, read/write/remove helpers, PID parsing, cheap liveness probe, command-line recognition, and full identity verification into that module.
- Keep
src/config.ts as a compatibility facade that re-exports the existing public symbols during the migration window.
- Migrate internal CLI, service, update, doctor, liveness, and restart callers to the leaf module in the same focused PR.
- Move process-state characterization into a dedicated test file while retaining config-path coverage where config ownership is the behavior under test.
- Update
structure/01_runtime.md and structure/02_config-and-codex-home.md to name the process-state owner and the boundary with OPENCODEX_HOME resolution.
Required invariants:
ocx.pid and runtime-port.json paths and serialized shapes stay compatible, including RuntimePortState.attestationSecret.
readPid() continues to require liveness plus the OCX start-command identity; readAlivePid() remains the cheap non-destructive probe; destructive callers continue to use verifyPidIdentity().
EPERM, WMIC-to-PowerShell fallback, Unix ps, timeouts, and the per-PID memo remain unchanged.
- expected-PID and snapshot-guarded removals keep their existing TOCTOU protections.
- writes continue through the existing atomic writer and config-directory hardening.
- no persisted config schema, provider, routing, GUI, or wording behavior changes.
Example usage or interface
import {
readAlivePid,
readRuntimePort,
verifyPidIdentity,
} from "./config/process-state";
Existing downstream imports remain valid for one migration window:
import { readAlivePid } from "./config";
Acceptance criteria:
- Characterization covers path resolution, malformed PID/runtime records, expected-PID filtering, concurrent rewrite guards,
EPERM, command-line recognition, and destructive identity verification.
- The dedicated process-state tests, remaining config tests, stale-state purge, proxy liveness/port reclaim, strict typecheck, and the full Bun suite are evaluated on the exact PR head.
- The leaf is the sole implementation;
src/config.ts contains compatibility re-exports only.
Alternatives or workarounds
- Leave process state in
src/config.ts: lowest churn, but preserves unrelated filesystem/process coupling.
- Move callers without a compatibility facade: rejected because external tests and downstream imports may rely on the existing exports.
- Create
src/runtime/ for this one leaf: possible later, but a new namespace is unnecessary for a behavior-preserving first slice; src/config/process-state.ts keeps path-resolution dependencies local and reviewable.
Additional context
[Decision Log]
- Purpose: isolate live process ownership from persisted config ownership without changing runtime behavior.
- Existing constraints: process state depends on the resolved OpenCodex home, atomic writes, directory hardening, and cross-platform process identity checks.
- Alternatives considered: keep the mixed module, perform a flag-day import rewrite, or introduce a second abstraction while retaining old helpers.
- Chosen approach: one leaf implementation under
src/config/, compatibility re-exports, characterization first, then internal import migration.
- Why: it reduces dependency breadth while preserving one source of truth and the current lifecycle contract.
- Tradeoff: a temporary facade remains, but the PR stays mechanical and independently reviewable from provider validation work.
This is separate from #2379 and should land in its own PR to keep two high-risk shared-module moves independently reviewable.
Checks
Area
Service lifecycle
What are you trying to accomplish?
Separate live proxy process-state ownership from persisted configuration ownership without changing startup, shutdown, status, service, update, or stale-state cleanup behavior.
What prevents this today?
src/config.tsowns both persisted config loading/writes and the live process records used by the proxy:ocx.pid,runtime-port.json, PID parsing, liveness checks, command-line identity verification, and snapshot-guarded cleanup. Internal callers that need only process state therefore depend on the broad config persistence module, and lifecycle correctness tests remain mixed into config tests.The ownership is also split across distant parts of the file: path helpers and PID/runtime-port I/O are not one contiguous block. A mechanical extraction must therefore be guided by symbols and tests rather than line ranges.
What should OpenCodex do?
RuntimePortState, read/write/remove helpers, PID parsing, cheap liveness probe, command-line recognition, and full identity verification into that module.src/config.tsas a compatibility facade that re-exports the existing public symbols during the migration window.structure/01_runtime.mdandstructure/02_config-and-codex-home.mdto name the process-state owner and the boundary withOPENCODEX_HOMEresolution.Required invariants:
ocx.pidandruntime-port.jsonpaths and serialized shapes stay compatible, includingRuntimePortState.attestationSecret.readPid()continues to require liveness plus the OCX start-command identity;readAlivePid()remains the cheap non-destructive probe; destructive callers continue to useverifyPidIdentity().EPERM, WMIC-to-PowerShell fallback, Unixps, timeouts, and the per-PID memo remain unchanged.Example usage or interface
Existing downstream imports remain valid for one migration window:
Acceptance criteria:
EPERM, command-line recognition, and destructive identity verification.src/config.tscontains compatibility re-exports only.Alternatives or workarounds
src/config.ts: lowest churn, but preserves unrelated filesystem/process coupling.src/runtime/for this one leaf: possible later, but a new namespace is unnecessary for a behavior-preserving first slice;src/config/process-state.tskeeps path-resolution dependencies local and reviewable.Additional context
[Decision Log]
src/config/, compatibility re-exports, characterization first, then internal import migration.This is separate from #2379 and should land in its own PR to keep two high-risk shared-module moves independently reviewable.
Checks