Skip to content

Fix task manager attribution for launchd-parented helpers - #4190

Merged
austinywang merged 5 commits into
mainfrom
issue-4189-task-manager-process-accounting
May 15, 2026
Merged

austinywang merged 5 commits into
mainfrom
issue-4189-task-manager-process-accounting

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented May 15, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fixes #4189.

This is focused on the WebKit/helper-process attribution gap called out from the broader Task Manager accuracy work in #4129. It does not touch the WKInspectorViewController crash path.

Changes

  • Parse cmux hooks codex monitor --workspace/--surface arguments as a fallback ownership signal when the helper has been reparented to launchd and environment-based scope is unavailable.
  • Require the exact cmux hooks codex monitor command shape for hook monitor attribution, including a real cmux executable in argv[0], and support both --workspace <uuid> and --workspace=<uuid> option forms.
  • Preserve ppid=1 WebKit WebContent accounting through explicit WebView root PID attribution and expose an attribution_reason on process rows.
  • Show attribution reasons in cmux top --processes tree output, including cmux-environment, cmux-hook-arguments, and webview-root-pid, so counted helper processes can be explained.
  • Add regression fixtures for launchd-parented codex monitor helpers, joined UUID options, exact command matching, and WebKit WebContent roots.

Local reproduction

  • ps showed 99 launchd-parented com.apple.WebKit.WebContent processes totaling about 6.3 GB RSS on this machine.
  • ps showed launchd-parented cmux hooks codex monitor processes with --workspace/--surface ownership arguments.
  • /Applications/cmux.app/Contents/Resources/bin/cmux top --processes --flat --format tsv showed WebKit roots already flowing through WebView PID roots, but hook monitor attribution depended on environment scope only.

Verification

  • Added the failing regression first in commit 58f244ef4.
  • Did not run local tests per repository policy; CI is the verification gate.

@vercel

vercel Bot commented May 15, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Canceled Canceled May 15, 2026 3:30am
cmux-staging Building Building Preview, Comment May 15, 2026 3:30am

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented May 15, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Tracks CMUX attribution reasons: derives scopes from environment or hook-monitor arguments, attaches attributionReason to process scopes and process-info, classifies each process in the process tree (including WebKit and root/child heuristics), and includes the reason in CLI output and tests.

Changes

CMUX Process Attribution Reasons

Layer / File(s) Summary
Attribution data model
Sources/CmuxTopSnapshot.swift
CmuxTopProcessInfo gains cmuxAttributionReason: String?. CmuxTopProcessScope stores attributionReason and its initializer accepts it. CmuxTopProcessSnapshot initializer made internal.
Scope derivation refactoring
Sources/CmuxTopSnapshotScopeCache.swift
cmuxScope(fromKernProcArgs:) now uses parsed arguments and environment. New cmuxScope(arguments:environment:) picks scope from environment (CMUX_WORKSPACE_ID/CMUX_TAB_ID, CMUX_SURFACE_ID/CMUX_PANEL_ID, reason "session-env") or hook-monitor args (detects cmux hooks codex monitor, extracts --workspace/--surface/--panel, reason "hook-monitor"). Helpers added for subcommand matching and UUID extraction.
Process tree attribution classification
Sources/CmuxTopSnapshot.swift
processTreePayload threads rootPIDs through processTreeNode. Added attributionReason(for:allowedPIDs:rootPIDs:) and isWebKitWebContentProcess(_:) to compute per-node "attribution_reason", preferring cmuxAttributionReason and falling back to root/WebKit/child/included classifications. processInfo(...) populates cmuxAttributionReason from the scope.
CLI attribution display
CLI/cmux.swift
Process attribution text is assembled from a parts array (process, pid, label) and conditionally appends a bracketed "[<attributionReason>]" when non-empty, then joins with spaces.
Attribution integration tests
cmuxTests/CmuxTopSnapshotScopeTests.swift
Adds two @MainActor tests validating hook-monitor and WebKit-root attribution when annotating top windows with includeProcesses: true. Adds firstSurface(in:), firstWebView(in:), and int(_:) test helpers.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

  • manaflow-ai/cmux#3290: Established the cmux top snapshot/model pipeline; this PR extends it with CMUX-derived attribution_reason fields and CLI formatting.

Poem

🐰 I hopped through args and env to find the cause,

I stitched each PID to its session without a pause,
From hooks to WebKit roots the reasons now show,
A tidy little trail where lost helpers go. 🥕

🚥 Pre-merge checks | ✅ 15 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (15 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: fixing task manager attribution for launchd-parented helpers, which is the primary focus of this PR.
Linked Issues check ✅ Passed The PR addresses all major coding requirements from issue #4189: fallback argument parsing for launchd-parented helpers, WebKit root PID attribution, and exposure of attribution reasons in process output.
Out of Scope Changes check ✅ Passed All changes are within scope of issue #4189: scope detection fallback logic, attribution reason tracking, WebKit root handling, and related test fixtures. No unrelated modifications detected.
Cmux Swift Actor Isolation ✅ Passed All production code properly maintains Swift 6 actor isolation: value models marked nonisolated, reference types properly isolated, tests appropriately use @MainActor.
Cmux Swift Blocking Runtime ✅ Passed No blocking/timing synchronization in production code. PR changes three production files with no blocking patterns. Blocking primitives only in test-only code (test fixture sleeps).
Cmux No Hacky Sleeps ✅ Passed PR contains only Swift changes. Check applies to TypeScript/JavaScript/shell; Swift is covered separately. No production sleeps detected.
Cmux Swift Concurrency ✅ Passed No legacy async patterns introduced. All new code is synchronous. @MainActor on XCTest methods is an allowed boundary pattern.
Cmux Swift @Concurrent ✅ Passed No Swift @concurrent violations. Heavy sysctl/parsing operations remain synchronous or properly hop to background tasks. First call uses withTaskGroup(.utility), second call is in nonisolated context.
Cmux Swift File And Package Boundaries ✅ Passed No Swift boundary violations. CmuxTopSnapshotScopeCache: 201 lines (under 400 threshold, coherent scope). Incidental touch to oversized files. Focused bug fix with clear extraction path.
Cmux Swift Logging ✅ Passed No logging violations detected. Modified source files contain no print, NSLog, debugPrint, or dump statements in app/runtime code. CLI output (JSON) is properly output via print as allowed.
Cmux User-Facing Error Privacy ✅ Passed Attribution reason labels are generic diagnostic terms that don't expose environment variable names or provider-specific flags, so they don't violate privacy rules.
Cmux Swiftui State Layout ✅ Passed No SwiftUI files or state patterns modified. PR contains only process snapshot data structures and caching logic with no SwiftUI imports or state attributes.
Cmux Architecture Rethink ✅ Passed Clear value-based architecture with single source of truth, pure deterministic functions, and no timing repairs, observers, side channels, or lifecycle splits.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR contains no user-visible NSWindow, NSPanel, NSWindowController, SwiftUI Window, or WindowGroup code. Changes are process attribution tracking, scope detection, and test-only fixtures.
Description check ✅ Passed The pull request description provides a clear summary of changes, references a specific issue (#4189), lists detailed changes, includes local reproduction steps, and mentions verification approach. It covers the main sections of the template.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-4189-task-manager-process-accounting

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 and usage tips.

@greptile-apps

greptile-apps Bot commented May 15, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes Task Manager attribution for helpers whose parent has been reparented to launchd (ppid=1). Scope detection now prefers CMUX_* environment variables and falls back to parsing cmux hooks codex monitor --workspace/--surface arguments, and every process in the snapshot carries an attribution_reason string displayed in cmux top --processes.

  • Scope derivation is refactored into cmuxScopeFromEnvironment + cmuxHookMonitorScope; containsSubcommandPath correctly limits URL.lastPathComponent normalization to argv[0] only, so path-valued subcommand lookalikes are rejected.
  • CmuxTopProcessInfo gains cmuxAttributionReason and the process payload gains attribution_reason; the attributionReason() helper classifies each node as webview-root-pid, explicit-root-pid, child-process, or included-process.
  • Regression tests cover the new argument-parsing path, the webview root case, and the launchd-parented monitor scenario using a public init on CmuxTopProcessSnapshot.

Confidence Score: 5/5

Safe to merge; the attribution and scope-detection logic is well-tested and does not touch resource accounting or process-tree membership.

The scope-detection refactor is mechanically straightforward: environment lookup is separated cleanly from argument parsing, the subcommand guard correctly applies path normalization only to argv[0], and the regression suite covers the key launchd-parented and WebKit WebContent cases. The two findings are about output verbosity and a loose name match — neither affects whether processes are attributed to the correct surface or whether resource totals are correct.

Sources/CmuxTopSnapshot.swift — the attributionReason() fallback and isWebKitWebContentProcess name check are worth a second look before shipping.

Important Files Changed

Filename Overview
Sources/CmuxTopSnapshotScopeCache.swift Refactors scope derivation into environment-first/argument-fallback pair; adds attributionReason to CmuxTopProcessScope. The subcommand path check correctly limits URL.lastPathComponent to argv[0] only.
Sources/CmuxTopSnapshot.swift Adds cmuxAttributionReason to CmuxTopProcessInfo and attribution_reason to process payloads; new attributionReason() helper always returns a non-empty fallback string, causing every process node to carry an internal categorization label in user-facing output.
CLI/cmux.swift topProcessLabel now appends [attributionReason] when non-empty; since attributionReason() never returns empty, every process in cmux top output carries a bracket label.
cmuxTests/CmuxTopSnapshotScopeTests.swift Adds regression fixtures for launchd-parented codex monitor and WebKit WebContent roots; covers joined/spaced option forms and executable-name guard.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Process sampled via sysctl] --> B{cachedCMUXScope returns non-nil?}
    B -- Yes --> C[CmuxTopProcessInfo cmuxAttributionReason = scope.attributionReason]
    B -- No --> D[cmuxAttributionReason = nil]
    C --> E[processTreeNode builds payload]
    D --> E
    subgraph scope
        S1{CMUX env vars present?} -- Yes --> S2[attributionReason = cmux-environment]
        S1 -- No --> S3{argv = cmux hooks codex monitor + flags?}
        S3 -- Yes --> S4[attributionReason = cmux-hook-arguments]
        S3 -- No --> S5[nil - no scope]
    end
    subgraph reason
        R1{cmuxAttributionReason non-nil?} -- Yes --> R2[return stored reason]
        R1 -- No --> R3{pid in rootPIDs and WebKit?}
        R3 -- Yes --> R4[webview-root-pid]
        R3 -- No --> R5{pid in rootPIDs?}
        R5 -- Yes --> R6[explicit-root-pid]
        R5 -- No --> R7{parentPID in allowedPIDs?}
        R7 -- Yes --> R8[child-process]
        R7 -- No --> R9[included-process]
    end
    E --> reason
    A --> scope
Loading

Reviews (3): Last reviewed commit: "fix: tighten hook monitor command matchi..." | Re-trigger Greptile

Comment thread Sources/CmuxTopSnapshotScopeCache.swift Outdated
Comment thread Sources/CmuxTopSnapshotScopeCache.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/CmuxTopSnapshot.swift`:
- Around line 55-61: Remove the misleading default for attributionReason in the
CmuxTopProcessScope/CmuxTopSnapshot init: change
init(workspaceID:surfaceID:attributionReason:) to require attributionReason (no
"= \"cmux-environment\"") so callers must supply an explicit reason; update any
call sites that relied on the default (verify cmuxScopeFromEnvironment and
cmuxHookMonitorScope still pass explicit reasons) and adjust any tests or
helpers to provide an intentional attribution string.
- Around line 418-423: The helper isWebKitWebContentProcess currently treats any
process whose name contains "WebKit" as a WebContent process, which incorrectly
matches other WebKit helpers; update the logic to only accept processes that are
actually WebContent by replacing the name check with a scoped check such as
name.localizedCaseInsensitiveContains("WebContent") (or stricter
equality/contains for "WebContent") and keep the existing path check for
com.apple.WebKit.WebContent so only processes matching WebContent by name or by
path are considered web content.

In `@Sources/CmuxTopSnapshotScopeCache.swift`:
- Around line 156-166: The uuidOptionValue(in:names:) helper currently only
handles flags where the name and value are separate argv entries; update it to
also recognize and parse "--name=VALUE" forms by checking each argument for a
prefix match with any name + "=", extracting the substring after the "=" (trim
whitespace), validating via UUID(uuidString:), and returning that UUID; retain
the existing logic that checks the next argv slot when the argument exactly
equals a name and continues to trim/validate as before (use the existing symbols
uuidOptionValue(in:names:), arguments, names, and UUID(uuidString:) to locate
and modify the function).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3a038587-dc2e-4725-b8f7-82ac6fc89b56

📥 Commits

Reviewing files that changed from the base of the PR and between c83f585 and 30d529d.

📒 Files selected for processing (4)
  • CLI/cmux.swift
  • Sources/CmuxTopSnapshot.swift
  • Sources/CmuxTopSnapshotScopeCache.swift
  • cmuxTests/CmuxTopSnapshotScopeTests.swift

Comment thread Sources/CmuxTopSnapshot.swift
Comment thread Sources/CmuxTopSnapshot.swift
Comment thread Sources/CmuxTopSnapshotScopeCache.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/CmuxTopSnapshotScopeCache.swift`:
- Around line 104-126: The attribution labels used in CmuxTopProcessScope are
inconsistent with the CLI contract: update the attributionReason passed in
cmuxScopeFromEnvironment(...) from "session-env" to "cmux-environment" and
update the attributionReason passed in cmuxHookMonitorScope(...) from
"hook-monitor" to "cmux-hook-arguments" so the emitted attribution_reason
matches the documented vocabulary; locate the CmuxTopProcessScope initializers
in those functions and replace the two literal strings accordingly.
- Around line 129-150: containsSubcommandPath currently allows a match when the
first N tokens equal the normalized path even if argv[0] isn't "cmux", so
tighten the check by requiring the executable name to be "cmux" for the
hook-monitor path match; update the function (containsSubcommandPath) to either
remove the fallback branch or add a guard that checks executableName == "cmux"
before comparing leadingArguments (use the existing executableName
extraction/normalization logic) so only invocations whose argv[0] lowercased
lastPathComponent equals "cmux" will return true.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 35fa2bc0-4393-4388-bd76-aacdf2f742d6

📥 Commits

Reviewing files that changed from the base of the PR and between 30d529d and 0d65f30.

📒 Files selected for processing (3)
  • Sources/CmuxTopSnapshot.swift
  • Sources/CmuxTopSnapshotScopeCache.swift
  • cmuxTests/CmuxTopSnapshotScopeTests.swift

Comment thread Sources/CmuxTopSnapshotScopeCache.swift
Comment thread Sources/CmuxTopSnapshotScopeCache.swift Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 0d65f30. Configure here.

Comment thread Sources/CmuxTopSnapshotScopeCache.swift Outdated
@austinywang
austinywang merged commit e5cfb91 into main May 15, 2026
35 checks passed
@austinywang austinywang mentioned this pull request May 19, 2026

This branch was successfully deployed

1 active deployment
Preview – cmux — ea398d33 Deployed May 15, 2026 by vercel[bot]
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.

Make Task Manager accurately report browser, WebView, and helper process memory

2 participants