One bridge process per provider artifact; delete the codex per-thread lane (WS4 L3) - #2239
Closed
SawyerHood wants to merge 1 commit into
Conversation
… lane (WS4 L3) The per-thread process key (#130) existed for a targeted account restart and for daemon lane isolation, not because codex app-server could not host two sessions; the codex bridge already supervises one app-server child per thread, and L1 moved the restart into it. The runtime now keys every provider's process by its artifact alone, the daemon mirrors one lane per provider per environment, and the per-thread reap no longer shuts a process down. A runtime test drives the real codex bridge with the fake app-server: N threads, one bridge, one child each; children die on stop, archive, and bridge retirement. Ratchet 142 → 136. Co-Authored-By: Claude <noreply@anthropic.com>
SawyerHood
deleted the
bb/ws4-l3-one-process-per-provider-thr_qdx8ashimg
branch
August 21, 2026 21:41
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WS4 layer 3 of 7, stacked on #2236 (L2). Design:
ideal-state-provider-api.md§"Runtime" — one process per provider artifact; the bridge supervises any children; the runtime never scopes processes per thread — and §7 "per-thread process keys … the daemon per-thread lane mirror".What was wrong
Codex alone ran one bridge process per bb thread:
resolveProviderProcessKeymintedcodex\0thread:<id>keys,isThreadScopedCodexProcessgated a per-thread shutdown inreleaseIdleProviderProcessand the idle reaper,EnsureProviderArgs.forThreadIdcarried the thread into process resolution, and the daemon'sgetProviderProcessLaneKeymirrored the lane with a\0thread:suffix for codex only. Every one of these was aproviderId === "codex"branch in core.Why the lane existed (the #130 finding).
git log -S CODEX_THREAD_PROCESS_KEY_PREFIXlands on one commit, 2a84ecf "Run Codex app-server per thread (#130)". Its PR body gives two reasons: (1) a targeted account restart — after a terminal 401/429 restart and resume only that thread's app-server before its next turn, so an externalcodex logintakes effect without a bb restart and without touching other threads; (2) daemon lane isolation — key host-daemon process lanes per codex thread sothread.stopdoes not block unrelated codex threads. Before #130 one app-server was shared per environment, so "codex app-server cannot host two sessions" was never the reason. Since the bridge migration the codex bridge supervises onecodex app-serverchild per bb thread itself, and L1 moved reason (1) into the bridge (rebuildBeforeNextTurnReason). Reason (2) is the one deliberate behavior change below.What changed
packages/agent-runtime/src/runtime.ts:resolveProviderProcessKeykeys on the provider artifact alone (<providerId>#bridge:<hash>[#acp:<fp>]); deletedCODEX_THREAD_PROCESS_KEY_PREFIX,isThreadScopedCodexProcess, the per-thread branch ofreleaseIdleProviderProcess(onlyretireSupersededBridgeProcessIfIdleremains, unchanged), andResolveProviderProcessKeyArgs.threadId. The idle reaper's open-work guard (backgroundWorkState.hasOpenThreadWork) now applies to the pre-experiment codex path too (it used to trust the thread-scoped process instead);CODEX_PROVIDER_IDsurvives in one place, the pre-experiment reap policy gate, which is product policy rather than topology.packages/agent-runtime/src/types.ts:EnsureProviderArgs.forThreadIddeleted (it would have been accepted-but-ignored).apps/host-daemon/src/command-router.ts:getProviderProcessLaneKeyreturns${environmentId}\0${providerId}for every provider;ProviderProcessLaneKeyArgs.threadIdand the daemon'sCODEX_PROVIDER_IDare gone. Behavior change: a codexthread.stopholds the codex process's write lane for the length of its dispatch, and another codex thread'sturn.submitin the same environment waits on it — exactly what claude and ACP threads do today. The lane covers the dispatch (until the runtime accepts the command), not the turn.threadUnarchiveBarrierskept as is (it serializes a thread, not a process). No wire field changed: noHOST_DAEMON_PROTOCOL_VERSIONbump.plugins/provider-codex/src/bridge/fake-codex-app-server.mjs:processLogPathlogsspawn:<pid>:<ppid>/exit:<pid>:<ppid>(also on SIGTERM), so a test can count children and the bridge processes that own them.runtime.ts6 → 3;command-router.tsdrops out of the core-file list).How you verified
The topology, stated from a test —
packages/agent-runtime/src/runtime.codex-topology.test.tsdrives the realplugins/provider-codexbridge module through the runtime (the daemon's path) with the fake app-server as its child:stopThread(t1)(release)archiveThread(t2)releaseAfter); bridge staysstopThread(t3)retireSupersededBridgeProcessIfIdleSIGTERMs it → itsonSigtermkills t3's child; runtime reports one expected bridge exitRuntime unit tests rewritten for the shared process (
runtime.process-lifecycle.test.ts): two codex threads answer from the same pid and a stop keeps the process; a failed construction keeps the process; the idle reap releases the session (thread/stop) and resumes on the same process (1 spawn, 0 exits). Daemon:routes every codex thread through the provider's one process lanereplaces the #130 exception test.Oracle (
--concurrency 4): typecheck 5/5; tests agent-runtime 451, host-daemon 555, provider-codex 201 — green. Parity codex 16 passed / 1 skipped, zero diffs. A4: 307 threads, 93,534 rows, zero diffs. Ratchet--base origin/main: 136 ≤ 142, baseline written.Part of the provider-plugin migration (WS4). L4 makes the skill roots one generic shape.