Skip to content

Fix Agent Hibernation never selecting live Claude Code sessions - #17306

Merged
teamleaderleo merged 2 commits into
manaflow-ai:mainfrom
KasperScript:fix/claude-hook-hibernation-scope
Oct 5, 2026
Merged

teamleaderleo merged 2 commits into
manaflow-ai:mainfrom
KasperScript:fix/claude-hook-hibernation-scope

Conversation

@KasperScript

@KasperScript KasperScript commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Live Claude Code sessions can now be hibernated. Before, every hook-backed agent entry reported containsUnrelatedProcess, so Agent Hibernation never selected one, on schedule or under memory pressure. Fixes #17305.

Claude is indexed from its hook record and never process-detected, so its entry never got the process-tree scope that detected agents get. SharedLiveAgentIndexLoader now passes RestorableAgentSessionIndex.load a provider that scopes a live hook PID against the same process snapshot, using agentHibernationProcessScope as detected agents do. The PID must be cmux-attributed to that panel. The entry takes that scope's termination set, identities and unrelated-process verdict. With no scope (other callers, or a PID not attributed to the panel), the entry stays unsafe exactly as before.

cmuxScopedProcessIDsByPanelKey() drops private so the loader can reuse it.

Related: #13834 makes resumed sessions idle and restorable. This change makes them pass the process-safety check after that.

Testing

Focused command, run locally on macOS 27 with Xcode 27:
./scripts/test-unit.sh -derivedDataPath <dir> -only-testing:cmuxTests/ClaudeHookSessionLivenessTests test

  • Red at b8f88fe6, the test-only commit: 5 tests ran and 1 failed. "A live Claude hook session in an exclusive pane is safe to hibernate" failed on terminationProcessIDs == [agentPID, mcpServerPID], containsUnrelatedProcess == false and processSafetyAllowsScheduledHibernation. The other 4 passed, including the new guard that an unrelated process in the pane keeps the session unsafe.
  • Green at af1445a0, the fix: 5 tests ran and all passed.
  • Related suites at af1445a0: AgentHibernation*, RestorableAgent*, SharedLiveAgentIndex*, AggregateMemoryRetention*, AgentResumeLiveness*, ForkParentFallback*, CompletedRestoredAgent* and ClaudeHook* (46 files). 340 Swift Testing tests in 40 suites passed.
  • python3 scripts/verify-local.py passed 17/17 static checks.
  • Local build note: the pinned Rust 1.88.0 can't load proc-macro crates with the Xcode 27 linker (E0463 can't find crate for tokio_macros). For these local runs only, I pointed Native/DiffSidecar/rust-toolchain.toml at stable 1.99. That change isn't part of this PR.
  • Not checked live: I haven't run a tagged runtime build, because that needs team dev credentials. The symptom it should fix: on 0.64.25, 11 off-screen idle Claude tabs with maxLiveTerminals 1 stayed running for 7+ minutes.

Changelog

Fixed: Agent Hibernation now hibernates idle background Claude Code sessions

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

🤖 Generated with Claude Code

https://claude.ai/code/session_01AsVtv4Ck96SHeGWAqLTG97

KasperScript and others added 2 commits October 4, 2026 19:37
A live Claude session in a pane with only its own process tree is still
marked as containing unrelated processes, so Agent Hibernation never
selects it. Add the failing regression plus a guard that a genuinely
unrelated process in the pane keeps the session unsafe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AsVtv4Ck96SHeGWAqLTG97
Claude Code is indexed from its hook record and never process-detected,
so its entry always reported containsUnrelatedProcess and failed the
process-safety check for scheduled and memory-pressure hibernation.

The loader now scopes a live hook PID against the same process snapshot
used for process-detected agents. The entry takes that scope's verdict
and termination set; without a fresh scope it stays unsafe as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AsVtv4Ck96SHeGWAqLTG97
@github-actions

github-actions Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

Thanks for opening your first cmux pull request!

We're a small team and the outside-PR queue is long, so a reply can take a while, sometimes longer than we'd like. If this one goes quiet and you'd like eyes on it, comment here and we'll pick it up.

A few things that help:

  • Start here covers what reviewers look for, what CI runs for you, and what happens next.
  • If the CLA check asks, reply with the sentence it gives you.
  • You don't need to run the app-host or UI tests locally. CI runs the ones your diff touches once a maintainer approves the first workflow run.
  • The verification ladder shows which checks fit your change. Say in the description which ones you ran.
  • If we end up fixing the same problem another way, we'll credit you with a Co-authored-by trailer and link the fix here.

@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: manaflow-ai/cmux/.coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 47c242d1-2e89-4163-a8b4-dfb10a93530a
📥 Commits

Reviewing files that changed from the base of the PR and between 186cec7 and af1445a.

📒 Files selected for processing (4)
  • Sources/RestorableAgentSession.swift
  • Sources/SharedLiveAgentIndexLoader.swift
  • Sources/VaultAgentProcessScanner.swift
  • cmuxTests/ClaudeHookSessionLivenessTests.swift

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The index loader now accepts panel-scoped process data for live hook-recorded agents. When a matching scope is available, the index uses it for process and hibernation details. Existing PID-only fallback behavior remains when the agent PID or scope is unavailable.

Changes

Claude hook session process scope

Layer / File(s) Summary
Provide panel-scoped process data
Sources/RestorableAgentSession.swift, Sources/SharedLiveAgentIndexLoader.swift, Sources/VaultAgentProcessScanner.swift
The loader derives panel-scoped process IDs and a hibernation scope, then supplies the scope to index loading when the panel IDs contain the hook agent PID.
Build and validate scoped hook entries
Sources/RestorableAgentSession.swift, cmuxTests/ClaudeHookSessionLivenessTests.swift
Live hook entries use the supplied scope for process and hibernation details. Tests cover a Claude MCP server in the termination group and an unrelated pane process. The PID-only fallback remains when the PID or scope is unavailable.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to af144

This change lets idle live Claude sessions be selected for hibernation while still blocking it when unrelated processes share the pane. No merge-blocking risk was found; CI should confirm the new tests pass.

Security Architecture Review

Security architecture risk: 🔵 Low · up to af144

Live Claude sessions can now enter a destructive hibernation path. The inspected design retains panel attribution, process-identity checks, unrelated-process rejection, and recovery safeguards. No introduced security defect was established, but native execution and failure recovery remain unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The newly reachable sensitive outcome is local process-group termination for a hibernating hook-backed session. The intended scope is bounded by agent descendants, permitted group membership, panel attribution, and terminal evidence; the displayed production changes add no privilege-elevation mechanism.

Security Findings and Attack Paths

  • inferred — No introduced bypass was established in the traced hook-record-to-signaling path. Hook metadata alone does not satisfy the inspected controls, and incomplete or unrelated scope evidence rejects hibernation. This bounded conclusion does not establish complete security coverage or race-free kernel execution.

Trust Boundaries and Controls

  • observed — The existing controller independently rechecks workspace ownership, lifecycle, panel and termination sets, identities, and confirmation generations after asynchronous snapshot work. Signaling additionally checks PID generations, process groups, TTY evidence, CMUX attribution, and group-leader identity before the final commit.

Resilience and Maintainability Implications

  • observed — Transcript snapshots precede termination, and teardown is revalidated after asynchronous work. Cleanup retains recovery material when the protected live file changes, while restore-monitor keys resolve symlink aliases to reduce competing recovery writers.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors, 1 warning)

Check name Status Explanation Resolution
Cmux Algorithmic Complexity ❌ Error The diff adds a redundant full process scan and sort on each live-agent index load. SharedLiveAgentIndexLoader.swift:92 builds cmuxScopedProcessIDsByPanelKey() after processDetectedSnapshots alr… Reuse the panel-to-process-ID map created by processDetectedSnapshots when constructing the hook scope provider. Pass the map through the loader path or return it with the detected snapshots, so each process snapshot is filtered and sorte…
Cmux Architecture Rethink ❌ Error The new hook scope is wired only through SharedLiveAgentIndexLoader. RestorableAgentSessionIndex.load makes the provider optional and defaults it to nil, while `RestorableAgentSessionIndex+Proce… Move hook-PID scope derivation into the shared process-census/index-loading owner, or route all fresh and cached index loads through one shared builder that receives the same process snapshot and derives detected-agent and hook-agent scopes…
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 4 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (22 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #17305 requires live hook-backed Claude sessions to pass hibernation safety for scheduled and memory-pressure reclaim. SharedLiveAgentIndexLoader now provides a scope only when the hook PID is…
Out of Scope Changes check ✅ Passed The changed loader, index, and process-snapshot visibility support the #17305 process-safety fix. Both added tests cover the required safe and unsafe pane cases. No unrelated changes appear in the rev…
Cmux Cloud Persistent Session And Early Input ✅ Passed The check is not applicable to this pull request. The authoritative diff changes hook-backed agent process scoping for hibernation, scanner visibility, and related tests. It does not change Cloud term…
Cmux Swift Actor Isolation ✅ Passed The production diff adds a synchronous hook-scope provider and a pure snapshot helper. The provider captures only the immutable panel-ID set and CmuxTopProcessSnapshot; it is called within the synch…
Cmux Swift Blocking Runtime ✅ Passed The PR adds no blocking or timing-based synchronization. The production diff adds a hook-process scope provider and computes process scope from the existing snapshot. The added lines contain no waits,…
Cmux Browser Automation Off-Main ✅ Passed The check does not apply to this PR. The repository rule covers browser socket automation in Sources/TerminalController.swift and ControlCommandExecutionPolicy.swift. The authoritative diff change…
Cmux Expensive Synchronous Load ✅ Passed The diff adds hook process scoping inside SharedLiveAgentIndexLoader.loadResultSynchronously(). Production reaches it through loadFreshResult(), which the shared cache starts in Task.detached; t…
Cmux Cache Substitution Correctness ✅ Passed The diff adds hook-process scoping from the same CmuxTopProcessSnapshot already used for process-detected hibernation scopes. SharedLiveAgentIndexLoader.loadFreshResult() obtains that snapshot wit…
Cmux No Hacky Sleeps ✅ Passed The check applies to production non-Swift runtime changes. The PR changes only four .swift files, including Swift tests, so it introduces no in-scope changes to assess for hacky sleeps.
Cmux Swift Concurrency ✅ Passed The changed Swift diff adds no background Dispatch work, Combine app state, completion-handler APIs, or fire-and-forget Tasks. The new hook-process scope provider is a synchronous closure used by the …
Cmux Swift @Concurrent ✅ Passed The diff adds no async, nonisolated async, or @concurrent declarations and does not remove an existing annotation. The new hook-scope provider and process-scope computation are synchronous. `Sha…
Cmux Swift Package Boundaries ✅ Passed The production diff adds app-target composition for hook-backed agents. SharedLiveAgentIndexLoader obtains panel-scoped PIDs from the current snapshot, verifies the hook PID belongs to that panel, a…
Cmux Swiftpm Lockfiles ✅ Passed The pull request changes only four Swift source and test files. It changes no Package.swift, Package.resolved, .gitignore, Xcode project, workflow, or dependency files, so the lockfile policy do…
Cmux Swift Logging ✅ Passed The changed Swift files add no logging statements or ad hoc diagnostic output. The diff adds process-scope logic and tests only. The repository policy applies to logging added or materially changed by…
Cmux User-Facing Error Privacy ✅ Passed The production diff changes hook-session process-scope indexing and hibernation-safety data only. It adds no user-facing error, alert, command output, API error body, or recovery copy. The changed sco…
Cmux Full Internationalization ✅ Passed The diff changes process-scope logic in three production Swift files and adds test cases. It adds no production user-facing text and changes no app string catalogs, Info.plist entries, web messages, m…
Cmux Swiftui State Layout ✅ Passed The diff changes process-indexing logic and adds tests. It does not introduce or expand SwiftUI views, state wrappers, geometry measurement, lazy/list row subtrees, or render-time state mutation. The …
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR adds no standalone window code. The diff changes hook-backed process-scope logic and tests; it adds or changes no NSWindow, NSPanel, NSWindowController, SwiftUI Window, or WindowGroup declarati…
Cmux Source Artifacts ✅ Passed The diff changes four tracked Swift files: three source files and one test file. The patches add hook-session scoping logic and regression tests. No changed path or patch content adds logs, screenshot…
Cmux No Test Or Debug Seam In Production Source ✅ Passed The production-source changes add no test/debug seam. cmuxScopedProcessIDsByPanelKey() becomes internal in Sources/VaultAgentProcessScanner.swift, and Sources/SharedLiveAgentIndexLoader.swift ca…
Title check ✅ Passed The title clearly identifies the main change: fixing Agent Hibernation so it can select live Claude Code sessions.
Description check ✅ Passed The description explains the problem, the behavior change, implementation, testing results, known limitations, and changelog entry. The subagent review checklist item remains unchecked, and no demo vi…
Full details: Cmux Algorithmic Complexity

Explanation

The diff adds a redundant full process scan and sort on each live-agent index load. SharedLiveAgentIndexLoader.swift:92 builds cmuxScopedProcessIDsByPanelKey() after processDetectedSnapshots already builds the same map at VaultAgentProcessScanner.swift:61. The map helper calls cmuxScopedProcesses() at VaultAgentProcessScanner.swift:795, which filters and sorts all process-snapshot entries at CmuxTopSnapshot.swift:245-248. This adds O(P log P) work per refresh for a scalable process collection, without a documented bound or measurement. The complexity rule rejects repeated filtering or sorting in process paths at roughly 1000 user-owned records.

Resolution

Reuse the panel-to-process-ID map created by processDetectedSnapshots when constructing the hook scope provider. Pass the map through the loader path or return it with the detected snapshots, so each process snapshot is filtered and sorted only once.

Full details: Cmux Architecture Rethink

Explanation

The new hook scope is wired only through SharedLiveAgentIndexLoader. RestorableAgentSessionIndex.load makes the provider optional and defaults it to nil, while RestorableAgentSessionIndex+ProcessCensus.swift calls load without it. The hibernation controller uses that separate path for memory-pressure evaluation and fresh teardown validation (AgentHibernationController+MemoryPressure.swift:39,97, AgentHibernationController+Teardown.swift:366, and AgentHibernationController+ProcessTermination.swift:210). Those entries retain the old unsafe fallback. Therefore the same hook session can have different process-safety state across index-loading paths, and fresh validation can still reject scheduled hibernation. The added tests exercise only SharedLiveAgentIndexLoader. This introduces a split process-scope owner and does not make the safety invariant hold across hibernation entrypoints, matching the rule against separately wired behavior.

Resolution

Move hook-PID scope derivation into the shared process-census/index-loading owner, or route all fresh and cached index loads through one shared builder that receives the same process snapshot and derives detected-agent and hook-agent scopes. Remove the optional nil-by-default path for production hibernation callers, or otherwise make their omission impossible. Update scheduled, memory-pressure, post-snapshot, and pre-signal validation paths to use that shared scope logic. Add tests that load the same hook-backed session through each production path and verify the same panel scope, termination identities, and unrelated-process result. The first migration cut is to make loadIncludingProcessDetectedSnapshots supply the hook scope and verify that the scheduled post-snapshot validation accepts the exclusive Claude session while rejecting a pane with an unrelated process.

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · 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.

@KasperScript

Copy link
Copy Markdown
Contributor 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 4, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator

Thank you @KasperScript, great root-cause writeup, and the red/green tests make this easy to trust :D CI is approved and running; we'll merge once it's green.

@teamleaderleo
teamleaderleo merged commit b047fa3 into manaflow-ai:main Oct 5, 2026
84 of 86 checks passed
@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Merge receipt for af1445a0a2: every check was green at merge (16 verified; 20 skipped by policy). Full suite runs on main after merge.

rustybret pushed a commit to rustybret/bmux that referenced this pull request Oct 5, 2026
7997c55 fix(cloud): update machine rename optimistically (manaflow-ai#17324)
c9bdbd6 Fix missing Terminals tab while Cloud machine connects (manaflow-ai#17326)
b047fa3 Fix Agent Hibernation never selecting live Claude Code sessions (manaflow-ai#17306)
9aefea4 cloud sidebar polish: header refresh, tab switch, empty states, errors and upgrade (manaflow-ai#17074)

# Conflicts:
#	.github/workflows/ci-guards.yml
#	.github/workflows/ci.yml
@teamleaderleo

Copy link
Copy Markdown
Collaborator

Merged, thank you @KasperScript! Agent Hibernation can now pick live Claude Code sessions again :D

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.

Agent Hibernation never selects live Claude Code sessions

2 participants