[BUG] Root cause identified: GrowthBook A/B flags tengu_permission_friction + tengu_quill_harbor silently override defaultMode:bypassPermissions via periodic server sync — macOS Desktop
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 cachedGrowthBookFeatures — disableBypassPermissionsMode 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
- Verify your account is in the harbor A/B test:
``bash`
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'))
"
tengu_harbor: True
If and tengu_quill_harbor: acceptEdits`, you are affected.
- Configure
~/.claude/settings.json:
``json``
{
"permissions": { "defaultMode": "bypassPermissions" },
"allowDangerouslySkipPermissions": true,
"skipDangerousModePermissionPrompt": true
}
- Start Claude Code Desktop → new session → send any message.
- Expected: session runs in Bypass Permissions.
- 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_brookinjecting system prompt content (same sync mechanism)
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗