Skip to content

Replay the launcher PATH into remote Claude Teams teammate panes - #10042

Open
asaflavi-baysecurity wants to merge 1 commit into
manaflow-ai:mainfrom
asaflavi-baysecurity:fix/7240-remote-teams-respawn-env
Open

Replay the launcher PATH into remote Claude Teams teammate panes#10042
asaflavi-baysecurity wants to merge 1 commit into
manaflow-ai:mainfrom
asaflavi-baysecurity:fix/7240-remote-teams-respawn-env

Conversation

@asaflavi-baysecurity

@asaflavi-baysecurity asaflavi-baysecurity commented Aug 12, 2026

Copy link
Copy Markdown

Summary

Four small pieces:

  1. claude_teams_respawn_env.go (new) — Go twin of ClaudeTeamsRespawnEnvironmentTransport on the same wire: base64 of a sorted-key JSON string map under the same CMUX_CLAUDE_TEAMS_RESPAWN_ENV_B64 key (json.Marshal sorts map keys, matching Swift's .sortedKeys). Decode reapplies the allowlist and fails closed — bad base64, bad JSON, a JSON array/string, non-string values, or a non-allowlisted key all yield nothing rather than a partial environment.
  2. agent_launch.gorunClaudeTeamsRelay — encodes its own fully configured environment at the same seam the Swift launcher's defer uses: after configureAgentEnvironment and configureClaudeNodeOptions, immediately before syscall.Exec.
  3. agent_launch.goconfigureAgentEnvironment — unsets the transport key and the two inherited trust-bypass keys (CLAUDE_CODE_SANDBOXED, CMUX_CLAUDE_TEAMS_SANDBOXED) for every relay, so cmux omo / omx / omc launched from inside a claude-teams process tree cannot inherit the lead's value and replay the lead's PATH — or the lead's trust-prompt waiver — into their own pane respawns. tmux_compat.go:1678 is a single shared respawn dispatch for every agent shim, so without this an inherited value would override the pane PATH built by ws_pty.go:757 and make tmux resolve to ~/.cmuxterm/claude-teams-bin instead of that agent's own shim dir. This mirrors clearInheritedClaudeLaunchEnvironment() (CLI/CMUXCLI+ExecutableResolution.swift:306-312), which the Swift side calls from both the claude-teams launcher and the omc launcher — as far as the Go side has the concepts: it clears inheritedTrustBypassKeys + the transport, and does not clear inheritedSessionIdentityKeys or CMUX_CLAUDE_TEAMS_WRAPPER_LAUNCH, neither of which the remote daemon reads or writes anywhere. The clear runs before cfg.extraEnv is applied, matching the Swift order (clearInheritedClaudeLaunchEnvironment()claudeTeamsExtraEnvVars). Placing it in the shared configureAgentEnvironment covers all four relays in one block; runClaudeTeamsRelay records its own value afterwards.
  4. tmux_compat.gotmuxClaudeTeamsRespawnEnvironment decodes the transport, overlays the existing CLAUDE_CODE_SANDBOXED opt-in exactly as before, and emits pairs in sorted key order, matching the Swift emitter's key-ordering rule.

The Go allowlist is deliberately PATH-only, and this is a real narrowing versus Swift — not parity. Swift's tmuxClaudeTeamsRespawnEnvironment also replays AgentLaunchEnvironmentPolicy.safeEnvironmentKeys (Packages/macOS/CMUXAgentLaunch/Sources/CMUXAgentLaunch/AgentLaunchEnvironmentPolicy.swift:58-119: CLAUDE_CONFIG_DIR, CLAUDE_SECURESTORAGE_CONFIG_DIR, ANTHROPIC_BASE_URL/MODEL, NODE_OPTIONS, …). The Go side replays PATH and nothing else, so the emitted command string matches the Swift one only in key ordering and quoting, not in content whenever a policy key is set. The narrowing is stated at the pointer in the code rather than claimed as a mirror. Rationale: AgentLaunchEnvironmentPolicy.swift is hand-written (scripts/generate-claude-launch-environment-policy.py emits only ClaudeSessionEnvironmentPolicy+Generated.swift and the TypeScript/wrapper outputs, from a manifest whose only keys are inheritedSessionIdentityKeys and inheritedTrustBypassKeys), so a hand-copied second allowlist in Go would silently drift from it. PATH is the only value a remote teammate needs replayed. If you'd prefer full policy parity, the right move is extending the generator to emit Go — a larger cross-cutting change I did not make unasked. Happy to do it if you want it.

No new precedence logic, no synthesized or home-relative directories, no two-tier resolution: the lead's PATH is copied verbatim, so a repaired remote teammate resolves exactly what the lead resolves.

Relationship to #7240 / #9150 / #9731

Verified repro evidence (for the symptom, on a build that predates #9731)

Reproduced on cmux 0.64.17 build 97 (a June build, before #9731):

  • ps eww on the app pid: cmux.app runs with launchd's bare PATH=/usr/bin:/bin:/usr/sbin:/sbin.
  • A teammate pane spawned mid-session via the lead's Agent tool (lead itself started by cmux claude-teams from a full-env terminal) got PATH=<TMPDIR>/cmux-cli-shims/<uuid>:/Applications/cmux.app/Contents/Resources/bin:/usr/bin:/bin:/usr/sbin:/sbin. gh, go, node, npm, task all exited 127 inside it; git/jq resolved only as Apple /usr/bin stubs.
  • Spawn chain observed live: /usr/bin/login -flp <user> /bin/bash --noprofile --norc -c exec -l /bin/sh -c '<abs path to claude> --agent-id … --agent-name …' — no login shell, so dotfiles cannot repair it.
  • The lead session was unaffected (it inherits the launching terminal's env).

This is evidence for the symptom's shape, not evidence that main is still broken locally — on main, #9731 fixes that scenario. The remote gap this PR closes is code-traced and unit-tested, not runtime-reproduced (see Testing).

Deliberately left alone (disclosed, not overlooked)

  • The Go relay still never grants the trust-prompt bypass: it has no --dangerously-skip-permissions argv check, so unlike the Swift launcher it never sets CLAUDE_CODE_SANDBOXED / CMUX_CLAUDE_TEAMS_SANDBOXED itself. Widening a trust-prompt bypass is your call, not a contributor's — this PR does not change that. What it does change is inheritance: configureAgentEnvironment now clears both keys alongside the transport, completing the mirror of clearInheritedClaudeLaunchEnvironment(), so a value inherited from an enclosing claude-teams process tree can no longer waive a different agent's prompt. The clear runs before cfg.extraEnv is applied, matching the Swift order, so a caller that ever does grant the opt-in still wins.
  • The transport is still not persisted into session restore (intentional per Fix Claude Teams teammate respawn environment #9731's rationale); a teammate still cannot spawn a teammate; command-carrying split-window / new-window / new-session remain uninjected, which keeps the whole mechanism coupled to Claude Code's current split-then-respawn sequence.

Testing

Run in daemon/remote with go1.26.5 darwin/arm64.

  • gofmt -l ./cmd/cmuxd-remote — clean (no files listed).
  • go build ./... — passes.
  • go vet ./... — passes.
  • go test ./... (the exact CI command, .github/workflows/ci.yml:313-315)ok github.com/manaflow-ai/cmux/daemon/remote/cmd/cmuxd-remote 5.180s. Green on main before the change and green after.
  • Transport unit tests: encode→decode round-trips PATH; a non-allowlisted key (ANTHROPIC_API_KEY, CMUX_SURFACE_ID) never crosses the boundary; an environment without PATH encodes nothing; and seven fail-closed decode cases (empty, non-base64, base64-of-non-JSON, JSON array, JSON string, non-string values, non-allowlisted key) each yield no values.
  • TestConfigureAgentEnvironmentClearsInheritedRespawnTransport — pins piece 3: with a lead's transport value in the ambient environment, configureAgentEnvironment for an omc-shaped config leaves the key unset and tmuxClaudeTeamsRespawnEnvironment() returns no pairs. Without the fix it fails with pairs = [{PATH /claude-teams/lead/bin}] — the lead's PATH leaking into another agent's respawn.
  • Behavior tests added to the existing TestTmuxCorpusRespawnPaneDispatchesSurfaceRespawn table, in the style of the neighboring claude-teams sandbox opt-in prepends env export case. They assert the full command param that the production dispatch path actually sends over surface.respawn — not a substring, so any change to the emitted quoting is caught:
    • valid transport → /bin/sh -c 'export PATH='"'"'/opt/homebrew/bin:/usr/bin:/bin'"'"'; claude --agent-id teammate-1', with tmux_start_command still raw for persistence;
    • garbage transport → no exports at all.
  • Negative checks that the tests pin the real contract, both re-run against the final tree: (a) restoring tmux_compat.go from origin/main fails …ReplaysTransportedPath, …OrdersKeysDeterministically and …/claude-teams_respawn_transport_prepends_the_launcher_PATH; (b) removing just the added unset block in configureAgentEnvironment fails TestConfigureAgentEnvironmentClearsInheritedRespawnTransport. Both pass again once restored. The tests exercise the production path rather than re-implementing its ordering.
  • -race scoped to the touched tests passes 3/3 consecutive runs. A wider -race -run 'TestTmuxCorpus' intermittently fails TestTmuxCorpusPRLaneSourcesExerciseRuntimeBehavior/regress/session-group-resize.shI ran that same command 4× on unmodified origin/main and it failed on 2 of 4 runs on the identical subtest, so it is a pre-existing flake in a PTY-driving corpus test this PR does not touch. CI runs plain go test ./..., not -race.

Test-isolation note worth knowing — this change widens it: TestTmuxCorpusRespawnPaneDispatchesSurfaceRespawn takes part of its input from the ambient environment, and this PR takes that sensitivity from one variable to two. The pre-existing one is CMUX_CLAUDE_TEAMS_SANDBOXED: on unmodified main, running the suite from inside a cmux claude-teams pane — where it leaks in as 1 — already fails 4 of that test's subtests. This PR adds a second variable with the same blast radius, CMUX_CLAUDE_TEAMS_RESPAWN_ENV_B64: with a lead's transport value in the environment those subtests would see an unexpected export PATH=… prepended to the command. So the pin is at the parent test rather than per-subtest — TestTmuxCorpusRespawnPaneDispatchesSurfaceRespawn now pins both variables once with t.Setenv at the top, which makes every one of its subtests immune to whatever launched the suite. The tests added here pin both as well, and the two pre-existing tests that call configureAgentEnvironment now save/restore all three keys that block clears, so the new production os.Unsetenv cannot leak process-wide into later tests. Verified: the package passes with CMUX_CLAUDE_TEAMS_SANDBOXED=1 in the environment, and with a valid CMUX_CLAUDE_TEAMS_RESPAWN_ENV_B64 in the environment.

What I could not verify: I have no SSH/remote cmux workspace, so I never observed a real remote teammate pane before/after. The claim "a remote claude-teams teammate gets no launcher PATH" rests on reading tmux_compat.go:1677 + :1763-1769 and agent_launch.go:26-76 / :407-417, plus gh pr view 9731 --json files showing no .go file in #9731. This PR does not claim to have fixed anyone's observed remote failure. Please sanity-check one remote claude-teams session before merging.

Which targets compiled locally: only daemon/remote (the sole module this PR touches) — built, vetted and fully tested here. No macOS app or Swift package target was compiled: this machine has Swift 6.3.2 CommandLineTools with no Xcode, so xcodebuild is unavailable. Since the diff is Go-only, no Swift target is affected; the Swift/app CI jobs will exercise unchanged code.

Demo Video

Not produced — I could not record one. Two reasons, both hard blockers on the authoring machine: the macOS app target cannot be built (Swift 6.3.2 CommandLineTools, no Xcode/xcodebuild), and I have no SSH/remote host to run a remote claude-teams session against, which is the only place this change is observable.

In its place, the verifiable evidence is:

  1. The exact emitted command string, asserted in full by a test that drives the production dispatch path:
    /bin/sh -c 'export PATH='"'"'/opt/homebrew/bin:/usr/bin:/bin'"'"'; claude --agent-id teammate-1'
    
  2. The two negative checks above, showing those tests fail against the unpatched production code and pass with it — including the leak output pairs = [{PATH /claude-teams/lead/bin}] for the cross-agent case.
  3. The full go test ./... output from the exact CI command.

Review Trigger (Copy/Paste as PR comment)

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

Checklist

  • I tested the change locally — gofmt, go build, go vet, and go test ./... (the exact CI command) in daemon/remote, plus two negative checks that the new tests fail without the production change. Not tested against a live remote host; see Testing.
  • I added or updated tests for behavior changes — unit tests for the transport (round-trip, allowlist, seven fail-closed cases), a test that an inherited transport does not survive into a non-claude-teams agent's respawn, and two subtests on the existing corpus table that assert the full command string the production path emits.
  • I updated docs/changelog if needed — no docs or changelog entry; this is an internal parity fix in the remote daemon with no user-facing surface or configuration change. Happy to add one if you'd like.
  • I requested bot reviews after my latest commit — the trigger block above is posted as the PR's first comment.
  • All code review bot comments are resolved — none yet.
  • All human review comments are resolved — none yet.

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

Replays the launcher PATH in remote Claude Teams teammate panes so tools resolve exactly like the lead session. Ports the respawn-environment transport to the remote Go daemon and prevents env leaks across agents.

  • Bug Fixes
    • Ported PATH replay to the remote tmux-compat path; teammate panes now export the lead PATH.
    • runClaudeTeamsRelay records CMUX_CLAUDE_TEAMS_RESPAWN_ENV_B64 (base64 JSON); tmuxClaudeTeamsRespawnEnvironment decodes it, preserves CLAUDE_CODE_SANDBOXED when opted-in, and emits env pairs in deterministic order.
    • configureAgentEnvironment unsets the transport and trust-bypass keys (CLAUDE_CODE_SANDBOXED, CMUX_CLAUDE_TEAMS_SANDBOXED) for all relays to avoid cross-agent PATH/trust leaks.
    • Go allowlist is intentionally PATH-only (narrower than Swift policy) to avoid drift; only PATH is replayed.
    • Added tests for transport round-trip and fail-closed cases, cross-agent isolation, and full surface.respawn command assertions.
    • Completes the remote half of the fix from Fix Claude Teams teammate respawn environment #9731; addresses the teammate PATH gap without changing the broader PATH behavior tracked in Teammate/agent panes get a minimal PATH — Homebrew (gh) and mise (node) commands not found #7240.

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

Review in cubic

Summary by CodeRabbit

  • New Features

    • Improved teammate respawns by preserving the required launcher environment, including PATH.
    • Added safeguards to isolate agent environments and prevent inherited sandbox or respawn settings.
    • Respawn commands now handle environment data consistently and deterministically.
  • Bug Fixes

    • Invalid or incomplete environment data is safely ignored.
    • Prevented stale configuration from affecting newly launched agents.
  • Tests

    • Added coverage for environment transport, filtering, cleanup, ordering, and respawn behavior.

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@asaflavi-baysecurity

Copy link
Copy Markdown
Author

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@cubic-dev-ai

cubic-dev-ai Bot commented Aug 12, 2026

Copy link
Copy Markdown

@codex review
@coderabbitai review
@greptile-apps review
@cubic-dev-ai review

@asaflavi-baysecurity I can't start this review because your workspace has reached its free monthly review limit. cubic has reviewed 245,275 of the 240,000 allowed lines of code this month. Reviews resume on 1 September 2026 (in 20 days). Paid plans include much higher monthly review limits. Upgrade now to resume reviews.

To help optimise your usage, you can tune cubic to get the most out of your usage limits:

Learn more →

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

@asaflavi-baysecurity I will review the changes in #10042.

⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ef57f61d-cc93-4f99-8467-98ae158fd011

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Claude Teams launch environments are encoded after configuration, cleared before new agent setup, and replayed by tmux respawns. The transport allowlists PATH, uses base64-wrapped JSON, fails closed on invalid data, and produces deterministic environment ordering.

Changes

Claude Teams respawn environment

Layer / File(s) Summary
Environment transport contract
daemon/remote/cmd/cmuxd-remote/claude_teams_respawn_env.go, daemon/remote/cmd/cmuxd-remote/claude_teams_respawn_env_test.go
Adds allowlisted environment encoding and decoding with sorted-key JSON, base64 transport, empty-value filtering, and fail-closed handling.
Launch environment recording and cleanup
daemon/remote/cmd/cmuxd-remote/agent_launch.go, daemon/remote/cmd/cmuxd-remote/agent_launch_context_test.go, daemon/remote/cmd/cmuxd-remote/claude_teams_respawn_env_test.go
Records the configured Claude Teams environment for teammate respawns and clears inherited sandbox and respawn variables during agent configuration.
tmux respawn environment replay
daemon/remote/cmd/cmuxd-remote/tmux_compat.go, daemon/remote/cmd/cmuxd-remote/tmux_compat_test.go, daemon/remote/cmd/cmuxd-remote/tmux_corpus_behavior_test.go, daemon/remote/cmd/cmuxd-remote/claude_teams_respawn_env_test.go
Decodes transported values, adds enabled sandbox state, sorts environment keys, and prepends valid PATH exports to respawn commands while ignoring invalid transport data.

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

Sequence Diagram(s)

sequenceDiagram
  participant ClaudeTeamsRelay
  participant AgentEnvironment
  participant RespawnEnvironmentCodec
  participant TmuxRespawn
  ClaudeTeamsRelay->>AgentEnvironment: configure current agent environment
  ClaudeTeamsRelay->>RespawnEnvironmentCodec: encode configured environment
  RespawnEnvironmentCodec-->>ClaudeTeamsRelay: return encoded transport value
  TmuxRespawn->>RespawnEnvironmentCodec: decode recorded environment
  RespawnEnvironmentCodec-->>TmuxRespawn: return allowlisted values
  TmuxRespawn->>TmuxRespawn: sort keys and build respawn exports
Loading

Suggested reviewers: austinywang, lawrencecchen

🚥 Pre-merge checks | ✅ 24 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (24 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed The PR diff contains only seven Go files under daemon/remote; no Swift paths or Swift patch changes were found, so Swift actor-isolation criteria are inapplicable.
Cmux Swift Blocking Runtime ✅ Passed The commit changes only Go files and Go tests; it introduces no production Swift diff, so the Swift blocking-runtime check is inapplicable.
Cmux Browser Automation Off-Main ✅ Passed The diff changes only seven Go files under daemon/remote/cmd/cmuxd-remote; no browser.* commands, WebKit/AppKit access, socket-worker routing, or policy tests are changed.
Cmux Expensive Synchronous Load ✅ Passed HEAD^..HEAD changes only Go files under daemon/remote/cmd/cmuxd-remote; no Swift code or expensive synchronous agent-history load was added or moved.
Cmux Cache Substitution Correctness ✅ Passed The commit changes only Go files under daemon/remote; no production Swift, TypeScript, or JavaScript diff is present, so this cache-substitution check is inapplicable.
Cmux No Hacky Sleeps ✅ Passed The diff changes only Go source and tests. This check covers TypeScript, JavaScript, shell, and non-Swift build/runtime scripts; no prohibited delay terms were added.
Cmux Algorithmic Complexity ✅ Passed The new production loops scan the environment once and sort only the allowlisted PATH plus optional sandbox key; no nested scalable scans or batch rescans were introduced.
Cmux Swift Concurrency ✅ Passed The diff changes only seven Go files under daemon/remote/cmd/cmuxd-remote; it introduces no Swift code or legacy Swift concurrency patterns.
Cmux Swift @Concurrent ✅ Passed The diff against origin/main contains only seven Go files under daemon/remote; no Swift file or Swift async isolation change is introduced, so this check is inapplicable.
Cmux Swift Package Boundaries ✅ Passed The diff contains only seven Go files under daemon/remote/cmd/cmuxd-remote; no Swift, SwiftPM, Xcode project, or package files changed, so the Swift boundary check is inapplicable.
Cmux Swiftpm Lockfiles ✅ Passed The diff contains only Go source and Go tests under daemon/remote; it changes no SwiftPM, Xcode, .gitignore, workflow, or dependency files covered by this check.
Cmux Swift Logging ✅ Passed The PR diff changes seven Go files only; it adds no Swift source or Swift logging statements, so the Swift logging check is inapplicable.
Cmux User-Facing Error Privacy ✅ Passed The production diff adds internal PATH transport and respawn handling, but no new error, alert, CLI output, API error body, or recovery text; vendor and environment names occur only in code or tests.
Cmux Full Internationalization ✅ Passed The commit changes only remote Go code and tests. It adds no Swift UI text, catalogs, web messages, metadata, or changelog copy; PATH and environment names are protocol/config tokens.
Cmux Swiftui State Layout ✅ Passed The parent-to-HEAD diff changes only seven Go files under daemon/remote; no Swift-family paths or added SwiftUI state/layout constructs are present.
Cmux Architecture Rethink ✅ Passed The PR diff contains only seven Go files under daemon/remote; it adds no Swift or SwiftUI/AppKit architecture changes covered by this check.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed The PR diff contains only seven Go files under daemon/remote; no Swift, NSWindow, NSPanel, SwiftUI Window, or WindowGroup changes are present, so this check is not applicable.
Cmux Source Artifacts ✅ Passed The diff changes only seven intentional Go source and test files under daemon/remote; no logs, binaries, screenshots, caches, temp folders, or artifact directories were added.
Cmux No Test Or Debug Seam In Production Source ✅ Passed The parent-to-HEAD diff changes only Go files under daemon/remote; it contains no Swift file under a production Sources path or test/debug seam.
Cmux No Ambient Global State ✅ Passed The diff against origin/main changes only seven Go files under daemon/remote; it introduces no production Swift changes covered by this check.
Description check ✅ Passed The description clearly explains the change and rationale, documents testing and limitations, includes the required sections, and updates the checklist accurately.
Title check ✅ Passed The title clearly and concisely describes the main change: replaying the launcher PATH in remote Claude Teams teammate panes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@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 `@daemon/remote/cmd/cmuxd-remote/agent_launch_context_test.go`:
- Around line 134-136: Update the test around configureAgentEnvironment to seed
non-empty values for CLAUDE_CODE_SANDBOXED, CMUX_CLAUDE_TEAMS_SANDBOXED, and
claudeTeamsRespawnEnvironmentKey before configuration, then assert each variable
is absent afterward while preserving the existing environment setup.

In `@daemon/remote/cmd/cmuxd-remote/agent_launch.go`:
- Around line 468-478: The cleanup in configureAgentEnvironment must not discard
the teammate sandbox opt-in needed by tmuxClaudeTeamsRespawnEnvironment.
Preserve CMUX_CLAUDE_TEAMS_SANDBOXED through an authoritative post-cleanup
source such as agentConfig.extraEnv, so configureClaudeTeamsShellWrapper can
restore it and respawns emit CLAUDE_CODE_SANDBOXED=1.
🪄 Autofix

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 Plus

Run ID: 99873d15-8ee4-4c99-9cf7-5b7921a50613

📥 Commits

Reviewing files that changed from the base of the PR and between b17c260 and d1b74dd.

📒 Files selected for processing (7)
  • daemon/remote/cmd/cmuxd-remote/agent_launch.go
  • daemon/remote/cmd/cmuxd-remote/agent_launch_context_test.go
  • daemon/remote/cmd/cmuxd-remote/claude_teams_respawn_env.go
  • daemon/remote/cmd/cmuxd-remote/claude_teams_respawn_env_test.go
  • daemon/remote/cmd/cmuxd-remote/tmux_compat.go
  • daemon/remote/cmd/cmuxd-remote/tmux_compat_test.go
  • daemon/remote/cmd/cmuxd-remote/tmux_corpus_behavior_test.go

Comment on lines +134 to +136
"CLAUDE_CODE_SANDBOXED",
"CMUX_CLAUDE_TEAMS_SANDBOXED",
claudeTeamsRespawnEnvironmentKey,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Assert that inherited sandbox state is removed.

The test only preserves these variables. It does not set non-empty values or verify that configureAgentEnvironment removes them. Seed CLAUDE_CODE_SANDBOXED, CMUX_CLAUDE_TEAMS_SANDBOXED, and claudeTeamsRespawnEnvironmentKey, then assert that all are absent after configuration.

Proposed test extension
 t.Setenv("PATH", "/usr/bin:/bin")
+ t.Setenv("CLAUDE_CODE_SANDBOXED", "1")
+ t.Setenv("CMUX_CLAUDE_TEAMS_SANDBOXED", "1")
+ t.Setenv(claudeTeamsRespawnEnvironmentKey, "transport")
 
 configureAgentEnvironment(agentConfig{
@@
 })
 
+ for _, key := range []string{
+   "CLAUDE_CODE_SANDBOXED",
+   "CMUX_CLAUDE_TEAMS_SANDBOXED",
+   claudeTeamsRespawnEnvironmentKey,
+ } {
+   if value, present := os.LookupEnv(key); present {
+     t.Errorf("%s leaked inherited value %q", key, value)
+   }
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"CLAUDE_CODE_SANDBOXED",
"CMUX_CLAUDE_TEAMS_SANDBOXED",
claudeTeamsRespawnEnvironmentKey,
t.Setenv("PATH", "/usr/bin:/bin")
t.Setenv("CLAUDE_CODE_SANDBOXED", "1")
t.Setenv("CMUX_CLAUDE_TEAMS_SANDBOXED", "1")
t.Setenv(claudeTeamsRespawnEnvironmentKey, "transport")
configureAgentEnvironment(agentConfig{
// existing configuration
})
for _, key := range []string{
"CLAUDE_CODE_SANDBOXED",
"CMUX_CLAUDE_TEAMS_SANDBOXED",
claudeTeamsRespawnEnvironmentKey,
} {
if value, present := os.LookupEnv(key); present {
t.Errorf("%s leaked inherited value %q", key, value)
}
}
🤖 Prompt for 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.

In `@daemon/remote/cmd/cmuxd-remote/agent_launch_context_test.go` around lines 134
- 136, Update the test around configureAgentEnvironment to seed non-empty values
for CLAUDE_CODE_SANDBOXED, CMUX_CLAUDE_TEAMS_SANDBOXED, and
claudeTeamsRespawnEnvironmentKey before configuration, then assert each variable
is absent afterward while preserving the existing environment setup.

Comment on lines +468 to +478
// Drop launch state inherited from an enclosing claude-teams process tree: its
// PATH must not replace this agent's own in a respawn, and its trust-prompt
// bypass must not waive this agent's own prompt. cfg.extraEnv is applied after
// this, and runClaudeTeamsRelay records its own transport once this returns.
for _, key := range []string{
"CLAUDE_CODE_SANDBOXED",
"CMUX_CLAUDE_TEAMS_SANDBOXED",
claudeTeamsRespawnEnvironmentKey,
} {
os.Unsetenv(key)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

ast-grep outline daemon/remote/cmd/cmuxd-remote \
  --items all --type function \
  --match 'configureClaudeTeamsShellWrapper|runClaudeTeamsRelay|tmuxClaudeTeamsRespawnEnvironment'

rg -n -C 5 \
  'CMUX_CLAUDE_TEAMS_SANDBOXED|CLAUDE_CODE_SANDBOXED|configureClaudeTeamsShellWrapper' \
  daemon/remote/cmd/cmuxd-remote -g '*.go'

Repository: manaflow-ai/cmux

Length of output: 22929


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- agent_launch.go: runClaudeTeamsRelay and cleanup ---'
sed -n '360,520p' daemon/remote/cmd/cmuxd-remote/agent_launch.go

printf '%s\n' '--- agent_launch.go: environment configuration and call order ---'
rg -n -C 12 \
  'configureAgentEnvironment|runClaudeTeamsRelay|extraEnv|syscall.Exec|CMUX_CLAUDE_TEAMS_SANDBOXED|CLAUDE_CODE_SANDBOXED' \
  daemon/remote/cmd/cmuxd-remote/agent_launch.go daemon/remote/cmd/cmuxd-remote/*.go

printf '%s\n' '--- agent_launch_shell.go: complete wrapper implementation ---'
cat -n daemon/remote/cmd/cmuxd-remote/agent_launch_shell.go

printf '%s\n' '--- respawn transport implementation ---'
sed -n '1735,1815p' daemon/remote/cmd/cmuxd-remote/tmux_compat.go

Repository: manaflow-ai/cmux

Length of output: 50372


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

printf '%s\n' '--- configureClaudeTeamsShellWrapper ---'
sed -n '1,145p' daemon/remote/cmd/cmuxd-remote/agent_launch_shell.go

printf '%s\n' '--- production references to sandbox opt-in ---'
rg -n -C 8 \
  'CMUX_CLAUDE_TEAMS_SANDBOXED|CLAUDE_CODE_SANDBOXED|sandbox' \
  daemon/remote/cmd/cmuxd-remote \
  -g '*.go' \
  -g '!**/*_test.go'

printf '%s\n' '--- repository-wide non-test references ---'
rg -n -C 5 \
  'CMUX_CLAUDE_TEAMS_SANDBOXED|CLAUDE_CODE_SANDBOXED' \
  -g '!**/*_test.go' \
  -g '!**/vendor/**'

Repository: manaflow-ai/cmux

Length of output: 9149


Preserve the sandbox opt-in for teammate respawns.

configureClaudeTeamsShellWrapper does not restore CMUX_CLAUDE_TEAMS_SANDBOXED. After configureAgentEnvironment clears it, tmuxClaudeTeamsRespawnEnvironment cannot emit CLAUDE_CODE_SANDBOXED=1. Pass the opt-in through agentConfig.extraEnv or another authoritative post-cleanup source.

🤖 Prompt for 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.

In `@daemon/remote/cmd/cmuxd-remote/agent_launch.go` around lines 468 - 478, The
cleanup in configureAgentEnvironment must not discard the teammate sandbox
opt-in needed by tmuxClaudeTeamsRespawnEnvironment. Preserve
CMUX_CLAUDE_TEAMS_SANDBOXED through an authoritative post-cleanup source such as
agentConfig.extraEnv, so configureClaudeTeamsShellWrapper can restore it and
respawns emit CLAUDE_CODE_SANDBOXED=1.

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.

claude-teams: teammate panes get the app's minimal PATH, breaking node-based Claude Code hooks

1 participant