Skip to content

Fix cmux agent hibernate for live Claude sessions - #18839

Draft
aryeh-stark wants to merge 1 commit into
manaflow-ai:mainfrom
aryeh-stark:fix/agent-hibernate-live-claude
Draft

aryeh-stark wants to merge 1 commit into
manaflow-ai:mainfrom
aryeh-stark:fix/agent-hibernate-live-claude

Conversation

@aryeh-stark

@aryeh-stark aryeh-stark commented Oct 9, 2026 •

Copy link
Copy Markdown

Summary

cmux agent hibernate (#15308) couldn't hibernate a live Claude Code session. On 0.65.0, every request for an idle, off-screen, restorable Claude was refused process_scope_unsafe, even a bare claude with only synchronous cmux hooks. With that fixed, the normal wrapped claude was still refused. After that, a hibernation that worked was reported as teardown_refused. Three bugs, all on this path; after this change the command hibernates both launches and cmux agent wake brings Claude back.

  1. The census index never scoped hook-backed agents. RestorableAgentSessionIndex.loadIncludingProcessDetectedSnapshots backs manual hibernation, memory-pressure reclaim and every teardown's pre-signal revalidation, but it calls load without hookProcessScopeProvider. A live hook record falls back to containsUnrelatedProcess: true. Fix Agent Hibernation never selecting live Claude Code sessions #17306 added the provider to SharedLiveAgentIndexLoader only. Both loaders now share SharedLiveAgentIndexLoader.hookProcessScopeProvider(processSnapshot:processArgumentsProvider:).
  2. The wrapper's inbox helper broke the shared-TTY rule. Claude Code runs the asyncRewake hook cmux hooks claude inbox-wait detached: a child of the agent leading its own session and process group, with no controlling terminal. agentHibernationProcessScope, signaling and exit observation all require one TTY. CmuxAgentHelperProcess now registers that argv. A process counts as a registered helper only if it is a direct child of the agent, runs the registered argv with CMUX_* env for this panel, has no controlling terminal, is alone in a process group it leads, and has no children. It stays in the termination set and is exempt only from the TTY rule. That evidence (cmuxHelperProcessIDs, ScopedProcessTermination.isCmuxHelper) is carried through the index entry and record, teardownIsStillSafe (must be unchanged), the final signal check (still detached, still the registered argv; its own group gets SIGTERM), and the refreshed exit epoch on the escalation and retry paths. e_tdev == NODEV now reads as no TTY, as KernelPortProcessTable already treats it, instead of a device every detached process shares.
  3. A successful manual hibernation was reported as a refusal. commitConfirmedTeardown returns false for .committedAwaitingExit, the normal outcome (SIGTERM sent, exit observed asynchronously), and hibernateNow maps any false to teardown_refused. A .manual teardown that commits now waits for its exit observation and succeeds only once the pane is .hibernated. Routine and memory-pressure batches are unchanged.

Still refused: a user's detached background job, an unregistered cmux hook (auto-name), the helper argv scoped to another surface, inside the agent's process group or with a child, and any change of process identity, group or terminal between scope and signal.

Testing

On a tagged Debug build of v0.65.0 (dda24fbd2) plus this change:

  • ./scripts/test-unit.sh test over 15 suites (ClaudeHookSessionLivenessTests, AgentHibernationProcessSignalBoundaryTests, AgentHibernationProcessSnapshotCoordinatorTests, CmuxTopProcessTreeTests, AgentHibernationProcessTerminationTests, AgentHibernationTerminationFailureTests, AgentHibernationBackgroundWorkTests, AgentHibernationManualRequestTests, SharedLiveAgentIndexAgentLivenessTests, AgentQuitTerminationCoordinatorTests, AgentHibernationPlannerSwiftTests, CmuxTopSnapshotScopeTests, AgentResumeLivenessTests, CompletedRestoredAgentGenerationTests, AgentHibernationTests): 170 tests passed.
    • New: a lone Claude is safe on both indexes; a Claude with its inbox helper is safe on both, the helper terminated with it; five kinds of detached work stay unsafe on both; signaling covers the helper's group and refuses a helper whose argv, surface or terminal changed; the refreshed epoch marks only a registered helper; NODEV is no TTY.
    • The new tests use seams this change adds, so a test-only commit can't compile. As the regression proof instead: with fix 1 reverted, the lone-Claude case fails on the census index only; with fix 2's TTY exemption reverted, the Claude-plus-helper case fails on the shared index.
    • Fix 3 has no unit test. It needs a live workspace, panel and census; the live check below covers it.
  • Live, in that tagged app, on an off-screen Claude Code 2.1.293 after an idle turn. Without this change both launches below were refused process_scope_unsafe. With it, each answered hibernated: true, then woke: true, with Claude's prompt back on screen in 2 to 7 s, over agent.hibernate/agent.wake on the socket and through cmux agent-hibernation hibernate|wake:
    • a bare claude with only synchronous cmux hooks;
    • the normal wrapped claude. Its teardown terminated Claude, the spool forwarder, two MCP servers and the inbox-wait helper, and none survived.

Not yet run on this branch's base (main cd85f00): building and testing it is in progress, so this PR is a draft. Main's ghostty and bonsplit pins differ from v0.65.0, and RestorableAgentSession.swift is the only one of these files main changed; the patch applies to it without conflict. Every other file that uses the changed types is identical to v0.65.0.

Changelog

Fixed: cmux agent hibernate hibernates an idle Claude Code session instead of refusing it as process_scope_unsafe, and no longer reports a completed hibernation as teardown_refused

Proof

CLI and socket only; terminal output to follow from the main-based tagged build.

Checklist

  • Behavior changes have added or updated tests, or Testing says why not
  • Reviewed with a subagent before merge (cmux-review), and all bot and human review comments resolved

Seen while verifying, not changed here

  • For about 15 to 20 s after a turn, Claude keeps a caffeinate -i -t 300 child with no CMUX_* env, so a manual hibernation in that window answers teardown_refused (the signal-time scope check, or a revalidation race when it exits). It succeeds once Claude stops it.
  • Hibernating and waking within 60 s of an earlier wake leaves the resume refused "another launch of this agent session is already starting": the earlier resume's AgentResumeLaunchGuard claim isn't released when its agent is hibernated, only after claimTTL.
  • A launch command captured with an empty argument (--setting-sources '') loses it, so the resume command fails.

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


Summary by cubic

Fixes cmux agent hibernate for live Claude Code sessions: previously every request for an idle, off-screen Claude was refused with process_scope_unsafe, and a hibernation that succeeded was reported as teardown_refused. After this change both bare and wrapped launches hibernate, and cmux agent wake brings Claude back.

Bug Fixes

  • The process-census index (manual hibernation, memory-pressure reclaim, pre-signal revalidation) never scoped hook-backed agents, leaving live hook records unsafe; it now shares the hook process scope provider with the shared live index.
  • Claude Code's asyncRewake hook (cmux hooks claude inbox-wait) runs detached with no controlling terminal, breaking the shared-TTY rule; it's now registered as a helper and terminated with its agent, exempt only from the TTY rule. e_tdev == NODEV now reads as no terminal.
  • Manual hibernation reported a successful commit as teardown_refused; it now waits for the exit observation and succeeds only once the pane is hibernated. Routine and memory-pressure reclaims are unchanged.

Still refused: a user's detached background job, an unregistered cmux hook, the helper argv scoped to another surface or inside the agent's process group, helpers with children, and any process identity, group, or terminal change between scope and signal.

Known limits, not changed here: a caffeinate child lingers ~15–20 s after a Claude turn so hibernation in that window still answers teardown_refused; waking within 60 s of an earlier wake is refused by the resume guard's claim TTL; a launch command captured with an empty argument loses it on resume.

Written for commit bf83891. Summary will update on new commits.

View guided diff

Manual hibernation refused every live Claude Code session as
process_scope_unsafe, and reported a hibernation that succeeded as
teardown_refused.

- Scope hook-backed agents in the process-census index too: manual
  hibernation, memory-pressure reclaim and the pre-signal revalidation read
  it, and it never supplied hookProcessScopeProvider (manaflow-ai#17306 fixed only
  SharedLiveAgentIndexLoader).
- Register cmux's detached `hooks claude inbox-wait` helper and terminate it
  with its agent, exempt only from the shared-TTY rule, carrying that
  evidence through scope, revalidation, signaling and exit observation.
  Read e_tdev NODEV as no TTY.
- Let a manual teardown that commits wait for its exit observation and
  report the pane's settled phase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 9, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@aryeh-stark

Copy link
Copy Markdown
Author

I have read the CLA Document v2.2 and I hereby sign the CLA

github-actions Bot added a commit that referenced this pull request Oct 9, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant