claude-code - 💡(How to fix) Fix [BUG] Root cause identified: GrowthBook A/B flags tengu_permission_friction + tengu_quill_harbor silently override defaultMode:bypassPermissions via periodic server sync — macOS Desktop

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

This is a root cause analysis for the cluster of issues where Claude Code Desktop ignores permissions.defaultMode: bypassPermissions and sessions always start in Accept Edits mode. The symptom is already reported in #61415, #61436, #61958, #39523 and many duplicates — but the specific mechanism has not been identified in any of those threads.

Short version: Two GrowthBook feature flags, pushed from Anthropic servers every ~9 minutes, silently override the user's settings.json and lock the Desktop app into Accept Edits mode.


Error Message

Blocks mode switching entirely. The UI shows the error:

Root Cause

When a user account is enrolled in the harbor permissions A/B test (tengu_harbor: true, tengu_harbor_permissions: true in ~/.claude.json's cachedGrowthBookFeatures), two additional flags are synced from Anthropic's GrowthBook CDN and cached locally:

Fix Action

Temporary workaround

Patch ~/.claude.json after each GrowthBook sync:

import json, os
path = os.path.expanduser('~/.claude.json')
d = json.load(open(path))
gb = d.setdefault('cachedGrowthBookFeatures', {})
gb['tengu_permission_friction'] = False
gb['tengu_quill_harbor'] = 'bypassPermissions'
json.dump(d, open(path, 'w'), ensure_ascii=False, indent=2)

Must be re-applied every time GrowthBook syncs (~9 min). A launchd WatchPaths agent can automate this.


Code Example

~/Library/Application Support/Claude/local-agent-mode-sessions/<session-id>/<workspace-id>/
  cowork-gb-cache.json
  local_<workspace-id>/.claude/.claude.json

---

python3 -c "
   import json, os
   d = json.load(open(os.path.expanduser('~/.claude.json')))
   gb = d.get('cachedGrowthBookFeatures', {})
   print('harbor:', gb.get('tengu_harbor'))
   print('harbor_permissions:', gb.get('tengu_harbor_permissions'))
   print('friction:', gb.get('tengu_permission_friction'))
   print('quill_harbor:', gb.get('tengu_quill_harbor'))
   "

---

{
     "permissions": { "defaultMode": "bypassPermissions" },
     "allowDangerouslySkipPermissions": true,
     "skipDangerousModePermissionPrompt": true
   }

---

import json, os
path = os.path.expanduser('~/.claude.json')
d = json.load(open(path))
gb = d.setdefault('cachedGrowthBookFeatures', {})
gb['tengu_permission_friction'] = False
gb['tengu_quill_harbor'] = 'bypassPermissions'
json.dump(d, open(path, 'w'), ensure_ascii=False, indent=2)
RAW_BUFFERClick to expand / collapse

Summary

This is a root cause analysis for the cluster of issues where Claude Code Desktop ignores permissions.defaultMode: bypassPermissions and sessions always start in Accept Edits mode. The symptom is already reported in #61415, #61436, #61958, #39523 and many duplicates — but the specific mechanism has not been identified in any of those threads.

Short version: Two GrowthBook feature flags, pushed from Anthropic servers every ~9 minutes, silently override the user's settings.json and lock the Desktop app into Accept Edits mode.


Environment

  • OS: macOS (Darwin 25.x, Apple Silicon)
  • Claude Code Desktop: 1.x (latest)
  • Claude Code CLI: 2.1.149–2.1.151
  • Platform: Anthropic API (personal account)

Root Cause

When a user account is enrolled in the harbor permissions A/B test (tengu_harbor: true, tengu_harbor_permissions: true in ~/.claude.json's cachedGrowthBookFeatures), two additional flags are synced from Anthropic's GrowthBook CDN and cached locally:

Flag 1 — tengu_permission_friction: true

Blocks mode switching entirely. The UI shows the error:

"Permission mode couldn't be changed. You can try again."

even when the user explicitly tries to switch to Bypass Permissions.

Flag 2 — tengu_quill_harbor: "acceptEdits"

Forces every new session to start in Accept Edits mode, regardless of permissions.defaultMode: "bypassPermissions" in settings.json. This is what makes the banner appear on every session start:

"Bypass Permissions mode isn't enabled. The session started in Accept Edits — enable Bypass Permissions in Settings to use it."

The harbor model introduces tengu_quill_harbor as the effective session default, which takes priority over the user-level settings.json defaultMode — effectively treating an A/B test flag as a managed/enterprise policy.


Why the standard workarounds fail

The Allow bypass permissions mode toggle (Settings UI)

Enabling the toggle sets allowDangerouslySkipPermissions: true in settings.json, which maps to --allow-dangerously-skip-permissions (adds bypass to the Shift+Tab cycle). This is not the same as --dangerously-skip-permissions (which actually starts the session in bypass mode). With tengu_permission_friction: true, even cycling via Shift+Tab is blocked.

disableBypassPermissionsMode is NOT set

Checked policy-limits.json and cachedGrowthBookFeaturesdisableBypassPermissionsMode is false (or absent). Bypass permissions is not disabled at the organization/managed level. The block comes exclusively from the two A/B test flags above.


Why it keeps coming back

GrowthBook syncs ~/.claude.json from Anthropic's servers approximately every 9 minutes (observed via file modification timestamps across multiple app sessions). Manually setting tengu_permission_friction: false and tengu_quill_harbor: "bypassPermissions" in ~/.claude.json restores correct behavior immediately — but the next GrowthBook sync resets them, reproducing the bug within minutes.

The sync also writes to session-specific cache files in:

~/Library/Application Support/Claude/local-agent-mode-sessions/<session-id>/<workspace-id>/
  cowork-gb-cache.json
  local_<workspace-id>/.claude/.claude.json

These session-level files may have different values than the main ~/.claude.json, causing inconsistent behavior (e.g., subagents running in a different mode than the parent session).


Reproduction

  1. Verify your account is in the harbor A/B test:

    python3 -c "
    import json, os
    d = json.load(open(os.path.expanduser('~/.claude.json')))
    gb = d.get('cachedGrowthBookFeatures', {})
    print('harbor:', gb.get('tengu_harbor'))
    print('harbor_permissions:', gb.get('tengu_harbor_permissions'))
    print('friction:', gb.get('tengu_permission_friction'))
    print('quill_harbor:', gb.get('tengu_quill_harbor'))
    "

    If tengu_harbor: True and tengu_quill_harbor: acceptEdits, you are affected.

  2. Configure ~/.claude/settings.json:

    {
      "permissions": { "defaultMode": "bypassPermissions" },
      "allowDangerouslySkipPermissions": true,
      "skipDangerousModePermissionPrompt": true
    }
  3. Start Claude Code Desktop → new session → send any message.

  4. Expected: session runs in Bypass Permissions.

  5. Actual: banner appears, session is in Accept Edits, mode switch fails.


Temporary workaround

Patch ~/.claude.json after each GrowthBook sync:

import json, os
path = os.path.expanduser('~/.claude.json')
d = json.load(open(path))
gb = d.setdefault('cachedGrowthBookFeatures', {})
gb['tengu_permission_friction'] = False
gb['tengu_quill_harbor'] = 'bypassPermissions'
json.dump(d, open(path, 'w'), ensure_ascii=False, indent=2)

Must be re-applied every time GrowthBook syncs (~9 min). A launchd WatchPaths agent can automate this.


Expected behavior

permissions.defaultMode: "bypassPermissions" in settings.json should be respected regardless of which GrowthBook A/B test cohort the user is enrolled in. A/B tests should not silently downgrade explicitly configured user permissions.


Suggested fix

In the harbor permissions model, when computing the effective session start mode, tengu_quill_harbor should be treated as a default that settings.json's defaultMode can override — not as a mandatory setting. Similarly, tengu_permission_friction should be false for any account where bypass permissions is not disabled at the managed/policy level.


Related issues

  • #39523 — [META] Bypass permissions mode is fundamentally broken (9-month trail)
  • #61415 — macOS: can't enable bypass, reverts to Accept Edits (25 comments)
  • #61436 — Desktop always starts in Accept Edits despite settings.json
  • #61958 — Notification fires on every session despite correct settings
  • #62061 — Related: GrowthBook flag tengu_heron_brook injecting system prompt content (same sync mechanism)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Temporary workaround

Patch ~/.claude.json after each GrowthBook sync:

import json, os
path = os.path.expanduser('~/.claude.json')
d = json.load(open(path))
gb = d.setdefault('cachedGrowthBookFeatures', {})
gb['tengu_permission_friction'] = False
gb['tengu_quill_harbor'] = 'bypassPermissions'
json.dump(d, open(path, 'w'), ensure_ascii=False, indent=2)

Must be re-applied every time GrowthBook syncs (~9 min). A launchd WatchPaths agent can automate this.


Expected behavior

permissions.defaultMode: "bypassPermissions" in settings.json should be respected regardless of which GrowthBook A/B test cohort the user is enrolled in. A/B tests should not silently downgrade explicitly configured user permissions.


Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING