Repository navigation
Description
What happened
"Compact idle threads" is set to "Before the cache expires (beta)" globally and on every Claude instance, but a long Claude thread (500K+ tokens) never got an idle /compact. It sat idle three times for 2 to 12 hours, and each time the prompt cache expired uncompacted. Every one of those idle spells started right after a run that T3 started for a "Delegated task ... completed" notification (a Codex child launched with delegate_task, mode async). Other threads on the same server do get idle compactions.
Diagnosis
IdleCompactionService.start arms a timer for every run.updated event with status: "completed", keyed by thread in a FiberMap, so any later such event for the thread replaces the waiting timer.
When a delegated-task notification run N completes, T3 then finalizes the delegated completion on the earlier run N-1 (the run that called delegate_task; the subagent's completionDelivery becomes delivered). That emits run.updated for run N-1, which is already completed, about 0.1 to 1 s after run N completed. FiberMap.run replaces the timer for run N with compactWhenDue(thread, N-1). That fiber computes the window for the latest run (N), hits if (window?.runId !== runId) return; and exits. Nothing re-arms a timer for run N, so the thread never compacts.
The listener is unchanged on main (apps/server/src/orchestration-v2/IdleCompactionService.ts, same run.updated/completed filter and thread-keyed FiberMap after #23929).
Possible fixes: ignore run.updated for a run that is not the thread's latest (or older than the run whose timer is waiting); or react only to the transition into completed; or, when window.runId is newer than runId, keep or re-arm the timer for window.runId instead of returning.
Steps to reproduce
- Settings: Compact idle threads = Before the cache expires (beta), on a Claude instance (subscription, 1 h cache).
- In a Claude thread above the compaction minimum (well over 100K tokens), have the agent call
delegate_task(async, any provider, e.g. Codex) and end its turn. - Wait for the child to finish. T3 posts "Delegated task ... completed" and the parent runs one more turn (run N) and ends it.
- Leave the thread idle for over an hour.
Expected: /compact about 2 minutes before the cache of run N expires.
Actual: no compaction. In orchestration_events, the last two run.updated events of the thread are run N completed, then run N-1 completed with delegatedCompletion set, a fraction of a second later.
Version
T3 Code Desktop 0.1.5 with its embedded local server 0.1.5 (commit 6d450a4). Same listener code on main as of 2026-10-31.
Environment
Windows 11 x64 (10.0.26200), desktop app with the local server; three Claude provider instances (claudeAgent driver, subscription accounts) with idleCompaction: beforeCacheExpires; children run on Codex through delegate_task. The server ran without restart for the whole period (started 2026-10-30 08:29Z), so this is not #27915.
Evidence
Events of the affected thread (UTC), from statev2.sqlite orchestration_events:
07:08:21.239 run.updated run 164 completed (notification run N)
07:08:21.980 subagent.updated delegated task (completionDelivery: delivered)
07:08:21.980 run.updated run 163 completed delegatedCompletion {disposition: open, nextGeneration: 2}
(no further events until the user returned at 10:32; no server:idle-compact receipt)
Same pattern before the other two idle spells: run 151 completed 15:24:52.224 then run 150 run.updated completed 15:24:53.214; run 153 completed 04:02:13.147 then run 152 run.updated completed 04:02:13.295.
Across all Claude threads on this server since its start, idle spells over an hour after a run triggered by a server-created message (delegated-task notifications, scheduled tasks): 0 compacted, 21 not. After runs triggered by MCP messages from other threads: 21 compacted, 22 not; after user messages: 12 compacted, 8 not. Only the delegated-task case was traced to the event level; the other misses may have the same or a different cause.
For the run that preceded the last idle spell, every condition in idleCompactionWindow() holds in the projections (not settled, archived or snoozed; message found by run id; no pending requests, merge-backs or background work; same provider thread and instance; 570K used tokens; promptCacheExpiresAt 1 h after completion).
Related issues
#27915 (idle compaction lost on server restart: different cause, same symptom). #21682 introduced the service, #23037 the per-instance setting, #23929 the cheaper eligibility check.
Fix applied or workaround
None applied. Workaround for users: send a message or a manual /compact after the last delegated-task notification before leaving the thread, or use delegate_task with mode: "wait" so the last run is not a notification run.
Filed by
Prepared with Claude Opus 5.5 via Claude Code in T3 Code (triage session); submitted by the owner.
Activity