[Bug]: Switching from an idle Claude thread to Codex still triggers automatic /compact#25943

opened4d agobyAAlb11747

What happened

On a long, idle Claude thread, selecting Codex and sending a message can still trigger T3's Claude cold-cache resume compaction first. T3 hands off to Codex, sends an automatic /compact on Codex with idleCompaction: "onReturn", and queues the user's actual message behind it. The stale Compact next pill can remain visible after selecting Codex.

Expected: switching away from Claude sends the original message through the normal provider handoff, without inheriting Claude's automatic compaction recommendation. Deliberate manual /compact should remain available. Returning to a saved, long, cold Claude conversation should evaluate that destination conversation and offer compaction when eligible.

Diagnosis

The parent chat view and composer resolve provider selection differently:

  • ChatView.tsx uses the existing conversation's lockedProvider.
  • ChatComposer.tsx uses modelPickerLockedProvider, which permits provider selection when handoff is supported.
  • Consequently, after selecting Codex, the parent can continue supplying a Claude resume-compaction recommendation while the composer's live getSendContext() correctly reports Codex. This persists after rendering; a particular React race is not required.
  • planComposerTurn receives an unscoped resumeCompactionTokens count, without destination provider/conversation identity.
  • useComposerSend uses that recommendation to send /compact with the actual selected model, then queues the user's message.

This is the client-side onReturn path, separate from the before-cache-expiry idle timer.

Steps to reproduce

Reproduced in the full web AppRoot with an in-process mocked environment, using the affected source revision:

  1. Open a stopped thread whose saved native Claude conversation has 250k context tokens and last activity older than its prompt-cache TTL. Enable the existing on-return compaction behavior.
  2. Confirm that the composer offers Compact next.
  3. Select Codex using the normal provider/model picker.
  4. Observe that the compaction pill remains visible.
  5. Enter an ordinary message and send it.
  6. Inspect the outgoing turn dispatches: the first is automatic /compact on Codex; the second is the original message queued behind it.

No model calls or database are needed for this reproduction.

Version

Observed installation: 0.1.3-nightly.20261026.3717.fork.2, source commit 54aeb9ba2df2f3ffbe288f529f1229b5d409b8f0.

The deterministic browser reproduction used that source revision. These results are not a claim of reproduction against the latest upstream HEAD.

Environment

T3 Code web composer on a Linux-hosted fork server; native Claude Agent provider as the source and Codex as the selected destination. The isolated verification used mocked provider dispatch and no provider CLIs.

Evidence

Two user-supplied production activity sequences showed Claude-to-Codex handoff followed by automatic Codex compaction before the actual message. In one, on 2026-10-26 UTC, the unwanted compaction ran from approximately 19:26:39 to 19:28:43; the actual message started afterward. The preceding Claude run had ended at 18:10:54, with no intervening Claude run for this compaction.

Independently reproduced outgoing dispatches, with fixture message text:

text
1Affected source, after selecting Codex:
21. text="/compact"
3 instanceId=codex
4 dispatchMode=start_immediately
5 idleCompaction=onReturn
62. text="Verify provider handoff without automatic compaction."
7 instanceId=codex
8 dispatchMode=queue_after_active
9
10Local fix, same selection:
111. text="Verify provider handoff without automatic compaction."
12 instanceId=codex
13 dispatchMode=start_immediately

Fix applied or workaround

A local fix aligns parent/composer selection, scopes the recommendation to the destination provider instance and saved conversation/context, hides mismatched suggestions, and revalidates immediately before dispatch after uploads/settings preparation. A refreshed or compacted conversation invalidates the old recommendation even when its conversation ID stays the same.

Validation completed:

  • 65 focused tests passed across ChatView.logic.test.ts, ContextWindowMeter.logic.test.ts, and useComposerSend.browser.test.tsx.
  • Regression tests failed before the relevant fixes. They cover stale parent Claude eligibility with live Codex selection; provider-instance/conversation mismatches; and conversation replacement, refreshed usage, or compaction while attachment upload is paused. Invalidated eligibility produces exactly one original message dispatch with its attachment and no forced compaction queue.
  • Controls cover intended same-Claude compaction, manual /compact, full-history opt-out, and warm/small context behavior.
  • An integrated mocked browser pass confirmed that Claude-to-Codex hides the pill and sends directly, while Codex-to-saved-cold-Claude shows the pill and retains the intended compaction/message sequence.
  • vp run check:changed checks passed, including formatting, lint, knip, affected typechecks, and the focused tests.
  • Two independent Codex reviews found send-preparation freshness gaps; both findings were addressed with regression coverage.

The proposed fix is now published on the fork's check branch: f2313c83895b1dfa4a305710e0c67c73f7cb5b06. After rebasing onto the fork containing upstream nightly v0.1.4-nightly.20261027.3770, all 63 current focused tests and vp run check:changed passed, and an independent Codex integration review approved the result. The earlier 65-test result above describes the original source baseline; upstream removed two existing cases. Exact-commit fork checks passed on c3f2943bafcdf5ad683dde3870ae4619889ba654, which includes the compaction fix plus a separate compatibility fix for an existing Git diagnostic failure in server cleanup. Both commits are now on fork main. Combined local checks passed 185 tests. The current-main fork release build passed all required checks and published 0.1.4-nightly.20261027.3770.fork.2, from descendant commit 311003447f01db2a2548b1661e638e462b91fac7, which includes this fix. Upstream PR: #26142 — scope resume compaction to the selected provider conversation. Its current upstream-only head bc2ce013fdf27d591919c8cc52d5b8d1f022fe50 also fixes optimistic queue display and anchoring after compaction becomes stale during preparation. All 65 focused tests, local checks, and all exact-commit CI jobs pass on this head. The preceding head 1fe0a60 passed all exact-commit CI jobs. Public before/after screenshots are attached in the PR. Installation and post-update runtime verification are still pending. Verification establishes client UI/dispatch behavior with mocked providers, not post-install execution against a paid model. No live database writes or paid model calls were used for verification.

Related issues

#23086 concerns switching back to a Claude provider instance and evaluating its own larger, cold saved context. That destination-scoping concern is related, but this report isolates the opposite failure: a stale Claude recommendation causes automatic compaction on a newly selected Codex destination.

This report does not infer billing amounts or quota consumption.

Filed by

GPT-6.1 Sol through the Codex harness in T3 Code. Investigation and independent reviews used OpenAI/Codex models only; the t3 triage launcher was not run.

Synced from GitHub

Proposed upstream fix: PR #26142.

The PR contains only the provider-scoped resume-compaction repair and its regression tests. It includes before/after screenshots and the reverse Codex-to-cold-Claude control. All 63 focused tests and exact-commit fork CI passed on 1fe0a60a58fcbb11e3f52663abf06ef91a9bdf98. No maintainer approval or post-install live-provider verification is claimed.