Skip to content

[Feature]: extract proxy process-state ownership from config persistence #2378

Description

@Ingwannu

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

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestserviceService lifecycle (WinSW/launchd/scheduler)

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions