Identified by Codex during the cross-review of M01 probe fix on PR #81 (msg codex_msg_5753c73beafc_197). These are real but lifecycle-class improvements — out of scope for the M01 must-fix, filed per consensus.
Codex 在 PR #81 M01 fix cross-review (msg ..._197) 时识别的三处仍引用 default 全局变量、对 multi-pair 语义不正确的 lifecycle 漏点。
1. state.ready === false paired error wording uses default globals
daemon.ts:1465-1471 consults the module-level codex.isSessionRestoreInProgress and module-level proxyTuiSlot when formatting the "still provisioning" error message for paired-not-ready chats. For a chat paired with a non-default pair, these globals reflect the WRONG pair's state — the error text could read "Shared Codex TUI thread is still provisioning" when the user's pair is healthy and only the default's isn't, or vice versa.
Fix: look up pairs.get(state.homePairId) and read its CodexAdapter + proxyTuiSlot when generating the wording.
2. detachClaudeWs pair-reap uses default proxyTuiSlot
When a paired Claude's WS detaches and pair-reap timer fires, the cleanup code references the module-level proxyTuiSlot. For a chat homed on a non-default pair, the wrong slot gets cleared. Real symptom likely: pair-reap doesn't release the correct pair's slot, blocking re-pairing on that pair.
Fix: route through pairs.get(state.homePairId)?.proxyTuiSlot (or the PairState getter/setter pair already exposes for default).
3. Pair exit event marks ALL chats not-ready
When one pair's codex app-server exits (crash, kill, OOM), every ChatState — not just chats homed on that pair — gets state.ready = false. This is a cross-pair contamination of crash isolation, the opposite of v2.3's design goal.
Fix: in the pair-exit handler, filter chats by state.homePairId === pair.pairId before flipping ready.
Acceptance
References
Identified by Codex during the cross-review of M01 probe fix on PR #81 (msg
codex_msg_5753c73beafc_197). These are real but lifecycle-class improvements — out of scope for the M01 must-fix, filed per consensus.Codex 在 PR #81 M01 fix cross-review (msg ..._197) 时识别的三处仍引用 default 全局变量、对 multi-pair 语义不正确的 lifecycle 漏点。
1.
state.ready === falsepaired error wording uses default globalsdaemon.ts:1465-1471consults the module-levelcodex.isSessionRestoreInProgressand module-levelproxyTuiSlotwhen formatting the "still provisioning" error message for paired-not-ready chats. For a chat paired with a non-default pair, these globals reflect the WRONG pair's state — the error text could read "Shared Codex TUI thread is still provisioning" when the user's pair is healthy and only the default's isn't, or vice versa.Fix: look up
pairs.get(state.homePairId)and read its CodexAdapter + proxyTuiSlot when generating the wording.2.
detachClaudeWspair-reap uses defaultproxyTuiSlotWhen a paired Claude's WS detaches and pair-reap timer fires, the cleanup code references the module-level
proxyTuiSlot. For a chat homed on a non-default pair, the wrong slot gets cleared. Real symptom likely: pair-reap doesn't release the correct pair's slot, blocking re-pairing on that pair.Fix: route through
pairs.get(state.homePairId)?.proxyTuiSlot(or the PairState getter/setter pair already exposes for default).3. Pair
exitevent marks ALL chats not-readyWhen one pair's codex app-server exits (crash, kill, OOM), every ChatState — not just chats homed on that pair — gets
state.ready = false. This is a cross-pair contamination of crash isolation, the opposite of v2.3's design goal.Fix: in the pair-exit handler, filter chats by
state.homePairId === pair.pairIdbefore flipping ready.Acceptance
References
ebea1d3+e929e12on PR feat: STM v2.3 multi-pair vertical upgrade + perf + bootstrap-reap fix #81codex_msg_5753c73beafc_197feat/multi-pair-probes(Patrick647/agent-bridge)