Skip to content

Multi-pair: three default-global lifecycle leaks (state.ready error, detachClaudeWs reap, pair-exit not-ready cascade) #83

@Patrick647

Description

@Patrick647

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

  • All three locations route through per-pair lookups (homePairId → pairs.get → that pair's globals)
  • Multi-pair probe coverage: extend M02 (kill one pair's codex) to assert OTHER pair's chats stay ready
  • Regression unit tests for each location
  • Probe M01-M02 still pass after change

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions