Skip to content

acpmux: an idle chat hibernates after 2 min, the one on screen too; typing wakes it - #26127

Merged
teamleaderleo merged 2 commits into
feat-cmux-nextfrom
hotswap-opus/session-hibernate
Oct 28, 2026
Merged

teamleaderleo merged 2 commits into
feat-cmux-nextfrom
hotswap-opus/session-hibernate

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Size: 1081 counted lines, of which 503 are tests and 58 docs; the code is 520. Hibernation, its wake and its gates only work together, and each test stays with the code it covers, so this lands as one PR.

Leo (2026-10-21), the ask this builds, word for word:

An idle Claude/Codex session (no turn running, nothing queued) hibernates after 2 minutes. Its process and its MCP sidecars exit, its state is checkpointed, and it wakes on the next prompt or click within about 1 s. Sessions waiting on a person stay cheap too: they keep only the question.

Also Leo: "WHY THE FUCK DO WE NOT HAVE MORE AGGRESSIVE FUKCING HIBERNATION" (10-21), and "We need to be able to, like, ultra-aggressively hibernate. And, like, when things wake up, you know..." (10-23).

Before

acpmux already had an idle exit (hub/idle.rs, since 2026-10-04), but it was 5 minutes and it skipped every chat a client was attached to. The app attaches the chats it shows and keeps the pane's chats attached, so the chats people actually leave open never hibernated: their Claude Code or Codex process and every MCP server it started stayed up until the daemon stopped.

After

  • An idle session hibernates after 2 minutes (hub::IDLE_CHILD, still overridable with ACPMUX_IDLE_CHILD_SECS; there is no settings page for it, so no new setting). Idle means: no turn running or waiting (Claude Code's own task-notification turns included), nothing queued, no pending permission or question, no start in progress, no background command or background subagent running, and nothing logged for 2 minutes.
  • A chat on screen hibernates too. Also never hibernated: an agent waiting on the answer to one of its own requests (live or recovered after a restart), a pending automatic continuation, a sub-session at work, and a Claude chat whose turns are not in Claude's store yet.
  • On screen: Only the agent's process goes: detach_child ends the harness's process group, and with it the stdio MCP servers it started. The session's record, transcript and agent session id stay. The pane is not touched (no reload, no new transcript row: the test checks that only status and stopped records are written, and the pane draws neither).
  • Wake: the next prompt resumes the conversation through the harness's own resume (claude --resume, ACP session/load), the existing path for a reaped or restarted session. The prompt is accepted and drawn at once, before the agent starts, as it already was. Typing into a hibernated chat (_acpmux/typing on, which the pane already sends on the first keystroke) or opening it on screen (_acpmux/attach shown, _acpmux/view visible; an authored collaborator view does not) starts the wake right away, so the agent is usually up by the time Enter is pressed. A hibernation survives a daemon restart (SessionMeta::hibernated_ms). A hibernation and its wake add no transcript row in the pane or the TUI (stopped/exited marked hibernated, resumed marked wake, and the replayed config record draw nothing). A wake respects the folder-trust gate like the warm path.
  • A failed wake fails its prompt the way any failed start does: a failed turn_result with the error, which the chat shows with Retry. The test covers that, and that the retry then wakes it.
  • Logs: every hibernation logs hibernated with cause=idle, how long it was idle and how long the exit took. Every wake logs woke (or wake failed) with its cause (typing, prompt, or another request that needs the agent), how long the start took, and how long it slept.

Never hibernated: a running turn (and so a running tool call or terminal command), a queued prompt, a pending permission or question, a background shell or subagent, a start holding the spawn lock, and any session while the daemon shuts down.

Scope (what this PR owns, and what it doesn't)

This PR owns the 2-minute idle threshold and what counts as idle, waking on a prompt or a click (typing), the wake logging, and the bench. It adds no kill or retirement code. The exit goes through the existing detach_child. The PID-only retirement foundation lands separately on its own item and this PR rebases onto it before landing: a bounded TERM, then KILL, of only the harness and MCP sidecar handles acpmux holds; the session lease plus generation fence; and the holder idle-generation query. Until then, the idle exit holds the session's turn_lock and spawn_lock while it ends the harness, so a prompt or a start can't race the exit. The lease replaces that hold on rebase.

Out of scope by design: detached work that escaped the harness (a setsid'd server) survives a hibernation, because nothing signals it.

Not in this PR

"Sessions waiting on a person stay cheap too: they keep only the question." A session waiting on a permission or question still keeps its agent here. Parking it means ending the waiting turn, keeping the question card live, and turning the answer into the woken agent's next input. That needs pane work and its own review, so it goes in a follow-up PR. This PR keeps those sessions awake, which is today's behaviour.

Timers

One task per hub, the existing idle reaper (idle_reaper_loop). It waits on the injected clock for the earliest idle deadline and on a notify. It sleeps with no deadline while no session has a live agent. It ends when the hub is dropped (its weak reference), and it stops for good when shutdown starts (stop_idle_reaper). No new timer. The typing wake is one task per wake that ends when the start ends. Typing also restarts the idle period, so a chat being typed into never hibernates.

Tests

tests/hub_integration/session_hibernate.rs, on the hub's injected clock, with the fake harness under a shell that starts a stand-in MCP server in its process group and records both pids:

  • 2 minutes idle, shown chat: the harness and the MCP server exit (checked by pid with ps), not before 2 minutes, and no transcript row is written;
  • a running turn with a tool call, a queued prompt, and a pending permission each keep it awake through 20 minutes; once they are done it hibernates;
  • a prompt wakes it: one new harness, the conversation resumed, and the prompt sent to the agent exactly once;
  • typing wakes it before the prompt, and the prompt then starts no second agent;
  • a failed wake fails its prompt with the error (Retry), and the retry wakes it.

lifecycle_fixes.rs: the old "an attached session's harness stays" assertion is now the opposite (an attached session hibernates too).

Proof

  • Tests (fleet Linux, exact head 05f3169, rebased onto the PID-only retirement foundation acpmux: an idle harness ends only when nothing but its MCP servers runs under it #26174): all of hub_integration 522 passed (1 ignored), acpmux --lib idle, transcript and store 72 passed, durable_agents idle 9 passed, clippy -D warnings clean.
  • The fleet refuses a changed allowlisted measure script until that script is on the base, so bench=hibernate (scripts/measure/acpmux-overhead.sh) runs after this lands. RSS before and after (a no-tools session) and the wake time are measured then and added here.

Risk

Medium. More harness restarts: a chat left open and idle for 2 minutes pays a resume on its next prompt. The typing wake hides most of that. Every resume runs the existing session/load / --resume path, the same one a daemon restart or the old 5-minute idle exit takes. A harness whose resume is slow (Claude Code with many MCP servers) makes the first prompt after a pause slower. The bench numbers above measure that.

Review (codex gpt-6-astra high, fast): SHIP at 05f3169 (earlier rounds at xhigh: SHIP at 8c76472 and at a93fbf4 after the rebase onto #26174; this round checks the lease drop after the stored mark and the swap to the foundation API).

Agent session: agent session

🤖 Generated with Claude Code

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 05f3169: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr26127 -f ref=hotswap-opus/session-hibernate -f platform=macos -f backend_mode=local

web and size checks: green

@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 27, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-nusmri created for this PR by the cmux coordinator.

@blacksmith-sh

blacksmith-sh Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Found 3 test failures on Blacksmith runners:

Failures

Test View Logs
build_only must be a conjunctive exclusion on the helper, signing, and publication jobs
, and must not gate the unsigned app build/
build_only must be a conjunctive exclusion on the helper, signing, and publication jobs
, and must not gate the unsigned app build
View Logs
nightly.yml must derive the embedded key and sign every appcast through the helper/
nightly.yml must derive the embedded key and sign every appcast through the helper
View Logs
test_the_nightly_is_covered (main.NightlyStepCoverageTests.test_the_nightly_is_cove
red)/
test_the_nightly_is_covered (main.NightlyStepCoverageTests.test_the_nightly_is_cove
red)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@cursor

cursor Bot commented Oct 27, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › No literal state roots (per-build isolation S7, cx-is1kf.8) is red on 13 of 21 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Orphaned: no activity for 13 h, and no lane holds a lease on it. The orphan sweep could not merge feat-cmux-next in: conflicts in cmux-tui/crates/acpmux/src/hub/hosts.rs, cmux-tui/crates/acpmux/src/hub/idle.rs, cmux-tui/crates/acpmux/src/hub/lifecycle.rs, cmux-tui/crates/acpmux/src/hub/mod.rs, cmux-tui/crates/acpmux/src/hub/permissions.rs, cmux-tui/crates/acpmux/src/store.rs. Listed for a lane to resolve.

…every hibernation and wake is logged

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-3891b16636fa80ffc495
@teamleaderleo
teamleaderleo force-pushed the hotswap-opus/session-hibernate branch from 8c76472 to a93fbf4 Compare October 28, 2026 10:31
@teamleaderleo
teamleaderleo merged commit 6388d2d into feat-cmux-next Oct 28, 2026
247 of 254 checks passed
@teamleaderleo
teamleaderleo deleted the hotswap-opus/session-hibernate branch October 28, 2026 13:27
@github-actions

Copy link
Copy Markdown
Contributor

The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/40697064264 at 3d58f37 failed: cmux-next checks (god files, concurrency, crash safety, l10n).
Those jobs last passed at 2a0fe48. This pull request is one of 3 merged since: #26865, #26873, #26127.
If the failure is in your change, fix forward on feat-cmux-next. Pull requests run only the tiers their paths reach (docs/ci/cmux-next-tiers.md); label a batch PR full-ci to run them all before merging.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

This branch had an error being deployed

1 failed and 2 inactive deployments
artifacts — 05f31697 Deployed Oct 28, 2026 by teamleaderleo via publish tree-addressed cmux-tui #71409
sentry-dsyms — 05f31697 Deployed Oct 28, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #71409
ci-routing-agent — 05f31697 Deployed Oct 28, 2026 by teamleaderleo via plan the macOS fallback build #71409
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant