Skip to content

fix: ensure teammateMode=bypassPermissions on native team setup - #998

Merged
namastex888 merged 1 commit into
devfrom
fix/ensure-teammate-bypass-permissions
Apr 2, 2026
Merged

namastex888 merged 1 commit into
devfrom
fix/ensure-teammate-bypass-permissions

Conversation

@namastex888

Copy link
Copy Markdown
Contributor

Summary

  • Fixes permission deadlock where subagents request Edit/Write approval from the team leader (an AI agent that can't approve), causing them to hang forever
  • Adds ensureTeammateBypassPermissions() to claude-settings.ts — idempotently sets teammateMode: "bypassPermissions" and skipDangerousModePermissionPrompt: true in ~/.claude/settings.json
  • Calls it from ensureNativeTeam() so the global setting is always correct when teams are active

Root Cause

CC's native team permission gate has two layers:

  1. Per-session --permission-mode bypassPermissions (genie already passes this)
  2. Global teammateMode in ~/.claude/settings.json (genie did NOT manage this)

When teammateMode: "auto", the global layer overrides the per-session flag, routing tool approvals to the team leader — which is a non-interactive AI agent, creating an unresolvable deadlock.

Test plan

  • bun run check — 1813 tests pass, 0 failures
  • Spawn a subagent via genie spawn and confirm it can use Edit without permission prompt
  • Verify ~/.claude/settings.json gets teammateMode: "bypassPermissions" after team setup

CC's native team permission gate uses the global `teammateMode` setting
in ~/.claude/settings.json as the authority for teammate permissions.
When set to "auto", tool approvals route to the team leader — which is
an AI agent that can't approve, causing a deadlock where subagents hang
forever waiting for Edit/Write permission.

Genie already passes `--permission-mode bypassPermissions` per-session
(provider-adapters.ts:251), but the global setting overrides it for the
native team layer. This adds `ensureTeammateBypassPermissions()` to
claude-settings.ts and calls it from `ensureNativeTeam()` so the global
setting is always correct when teams are active.

Also ensures `skipDangerousModePermissionPrompt: true` so agents spawned
with `--dangerously-skip-permissions` don't hit interactive prompts.
@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 68d27d80-c9d0-4f99-ab78-09dccd232a8f

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ensure-teammate-bypass-permissions

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7c21301a6a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +64 to +65
const dir = join(homedir(), '.claude');
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Respect CLAUDE_CONFIG_DIR when writing teammate settings

ensureTeammateBypassPermissions() now hardcodes ~/.claude, so when CLAUDE_CONFIG_DIR is set (which this codebase supports for team paths and tests), it updates the wrong settings file and can still leave the active Claude config in teammateMode: auto. In those environments, the deadlock fix does not apply and the function may also unexpectedly mutate a real home-directory config.

Useful? React with 👍 / 👎.

Comment on lines +77 to +78
if (settings.teammateMode !== 'bypassPermissions') {
settings.teammateMode = 'bypassPermissions';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Guard parsed settings shape before property writes

The new parser path assumes settings.json is an object, but valid JSON like null, a string, or a number will pass JSON.parse and then throw at settings.teammateMode/assignment in strict mode. That turns a recoverable malformed-settings case into a hard failure during team setup instead of applying the intended safe defaults.

Useful? React with 👍 / 👎.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a mechanism to automatically configure Claude settings to bypass permissions for native teams, preventing deadlocks during tool approvals. The feedback highlights several areas for improvement: using existing constants for directory paths to maintain consistency and respect environment variables, addressing potential race conditions during the read-modify-write cycle of the settings file, and converting synchronous file operations to asynchronous ones to avoid blocking the event loop in an otherwise async module.

Comment on lines +64 to +65
const dir = join(homedir(), '.claude');
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The directory path is already defined as a module-level constant CLAUDE_DIR at line 12. Re-calculating it here using homedir() is redundant. Additionally, note that claude-native-teams.ts respects the CLAUDE_CONFIG_DIR environment variable, while this file hardcodes the path. Using the existing constant helps maintain consistency within the file.

Suggested change
const dir = join(homedir(), '.claude');
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });

Comment on lines +70 to +87
settings = JSON.parse(readFileSync(CLAUDE_SETTINGS_FILE, 'utf-8'));
} catch {
// Corrupted file — overwrite with safe defaults
}
}

let changed = false;
if (settings.teammateMode !== 'bypassPermissions') {
settings.teammateMode = 'bypassPermissions';
changed = true;
}
if (settings.skipDangerousModePermissionPrompt !== true) {
settings.skipDangerousModePermissionPrompt = true;
changed = true;
}

if (changed) {
writeFileSync(CLAUDE_SETTINGS_FILE, `${JSON.stringify(settings, null, 2)}\n`, 'utf-8');

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This Read-Modify-Write (RMW) cycle on the global settings.json file is susceptible to race conditions. If multiple processes (e.g., multiple Genie instances or a user manually editing settings) attempt to update the file simultaneously, one process might overwrite changes made by another because the entire file is read into memory, modified, and then written back. For a shared configuration file, consider using a file lock or an atomic update mechanism to prevent potential data loss of other settings.


// Ensure the global teammateMode is bypassPermissions so the native team
// permission gate doesn't route tool approvals to the leader (deadlock).
ensureTeammateBypassPermissions();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The function ensureTeammateBypassPermissions is synchronous and performs blocking file I/O. Since it is called within the async function ensureNativeTeam, and this module otherwise uses node:fs/promises for filesystem operations, it would be more consistent to implement this as an asynchronous operation and await it here to avoid blocking the event loop.

@namastex888
namastex888 merged commit 795636a into dev Apr 2, 2026
6 checks passed
@namastex888
namastex888 deleted the fix/ensure-teammate-bypass-permissions branch April 10, 2026 16:18
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