Skip to content

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

Closed

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.

Activity

  1. lidge-jun commented on Aug 22, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 46 / 80

    설명: 이 이슈는 설정 파일 일과 프록시가 살아 있는지 보는 일을 src/config.ts 에서 가르자고 한다. 지금 CURRENT dev HEAD 는 378d889c5 이다. 이번 시간에 origin/dev 가 af5dd16 에서 여기로 옮겼다. 착지한 코드는 #2359 호출 불가 모델 제외(Closes #2330), #2376 번 1.4 메모리 문서+하니스, #2377 WP7 기록이다. package.json 은 2.27.0 이다. 지금 HEAD 의 src/config.ts 는 3975줄이다. 이슈가 말한 약 240줄짜리 한 덩어리는 지금 파일이 아니다. getPidPath 와 getRuntimePortPath 는 1663줄 근처다. writePid 는 3446줄이다. verifyPidIdentity 는 3862줄이다. 경로 헬퍼와 실제 읽기쓰기가 떨어져 있다. resolvePidPath 는 624줄이다. src/runtime 폴더는 지금 HEAD 에 없다. 이미 있는 잎은 src/config/provider-name.ts 와 src/types.ts 배럴+src/types/* 이다. RuntimePortState 에는 attestationSecret 이 있다. 이 값은 설정 디렉터리로 보호하고 밖으로 안 보낸다. readPid 는 살아 있음과 ocx 시작 명령 확인을 같이 한다. readAlivePid 는 싼 확인만 한다. 지우는 쪽은 verifyPidIdentity 를 쓴다. EPERM, 윈도 WMIC 다음 파워셸, 유닉스 ps, PID 메모, 스냅샷 가드 삭제가 여기 있다. 호출자는 src/cli/index.ts 시작과 종료, src/cli/status.ts, src/cli/doctor.ts, src/service.ts, src/update/index.ts 와 job.ts 다. 테스트는 tests/config.test.ts 와 tests/stale-state-purge.test.ts, tests/proxy-liveness.test.ts 에 섞여 있다. 이슈는 동작을 바꾸지 말라고 했다. 시작 설계를 다시 하지 말라고 했다. config.ts 를 호환 창구로 남겨 공개 이름을 한동안 재수출하라고 했다. 사용자 길이 버그가 아니라 모듈 경계 일이라서 46.

    src/config.ts 라인 1663 - getPidPath. 이슈의 240줄 덩어리 시작으로 적힌 자리. 실제 쓰기는 훨씬 아래다
    src/config.ts 라인 3446 - writePid. 설정 디렉터리 가드와 atomicWriteFile 을 쓴다. 설정 저장과 같은 파일을 만진다
    src/config.ts 라인 3458 - RuntimePortState. attestationSecret 이 있다. 옮길 때 이 필드를 빠뜨리면 안 된다
    src/config.ts 라인 3843 - readAlivePid 는 싼 확인. 라인 3862 verifyPidIdentity 는 지우는 쪽 확인. 둘을 합치면 안 된다
    src/runtime - 지금 HEAD 에 폴더가 없다. 이슈가 예시로 든 경로는 새로 만들어야 한다

    메인테이너의 판단이 필요한 지점

    • 잎을 src/runtime/process-state.ts 로 새 폴더에 둘지, 이미 있는 src/config/ 옆에 둘지
    • config.ts 재수출을 얼마나 오래 남길지. 외부 테스트가 config 에서 이 이름을 직접 가져간다
    • [Feature]: extract provider validation from config persistence #2379 제공자 검사 추출과 같은 주에 할지. 같은 PR 에 넣으면 리뷰가 커진다

    너의 추천
    새 PR 을 연다. 먼저 특징 테스트를 옮기고, 그다음 잎 하나와 config.ts 재수출만 넣는다. 동작은 한 줄도 바꾸지 않는다. src/runtime 은 지금 없으니 폴더를 만들지 기존 src/config 패턴을 따를지는 메인테이너가 고른다. #2379 와 같은 PR 에 넣지 않는다. 지금 닫지 말 것. types.ts 스플릿과 겹치면 닫고 리베이스하지 않는다. 라벨은 그대로 둔다. 프리뷰 배포가 아니다.

    이 댓글은 grok-bot이 작성했습니다

  2. changed the title [-][Architecture]: extract proxy process-state ownership from config.ts without behavior changes[/-] [+][Feature]: extract proxy process-state ownership from config persistence[/+] on Aug 22, 2026
  3. Ingwannu commented on Aug 22, 2026

    @Ingwannu
    OwnerAuthor

    Implemented the behavior-preserving extraction in draft PR #2387 against dev.

    The PR keeps src/config.ts as the compatibility facade, moves the single implementations into src/config/{paths,atomic-write,process-state}.ts, migrates lifecycle-only callers, adds dedicated process-state characterization, and updates the runtime/config structure documents.

    Exact-head verification completed:

    • focused process/config/lifecycle suites: 420/420
    • real-home write guard: 9/9
    • strict typecheck: passed
    • git diff --check: passed
    • full suite: 14,344 pass / 16 skip / 1 unchanged timing-only failure under the two-core cap; tests/request-pacing.test.ts passes 14/14 on both the PR head and clean current dev when rerun in isolation

    I left the PR as draft and requested @lidge-jun because the author cannot approve or merge their own PR. This is a TypeScript compatibility/import-boundary refactor on the repository's sole integration line, dev.

  4. lidge-jun commented on Aug 23, 2026

    @lidge-jun
    Owner

    Closed by #2387, squash-merged to dev as b6c7c0a.

    OpenCodex-home paths, atomic writes, and PID/runtime-port ownership now live in three acyclic config leaf modules, with src/config.ts retaining its compatibility exports. Lifecycle, management, service, OAuth, and update callers import process state directly and their destructive identity checks are unchanged. Independent review found no dropped runtime export, no import cycle, and no core-to-Lab edge; 472 pass / 0 fail across eight suites, a synthesized current-dev merge at 473 / 0 with typecheck green, and CI 29 pass.

    Two test-ownership follow-ups are recorded in the devlog rather than held against the refactor: tests/process-state.test.ts does not yet exercise readAlivePid(), verifyPidIdentity(), or the EPERM behavior this issue asked for, and tests/config.test.ts:2216 still duplicates process-state characterization.

    Closing by hand because PRs here target dev.

  5. lidge-jun commented on Aug 23, 2026

    @lidge-jun
    Owner

    Landed via #2387 at b6c7c0a

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

    enhancementNew feature or requestlanded-via-maintainerOriginal PR closed after landing via a maintainer merge trainserviceService lifecycle (WinSW/launchd/scheduler)

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions