Skip to content

fix(permissions): user settings tier follows $CLAWCODEX_CONFIG_DIR; say what the approval menu changes - #822

Merged
agentforce314 merged 1 commit into
mainfrom
fix/permission-settings-scope
Aug 9, 2026
Merged

fix(permissions): user settings tier follows $CLAWCODEX_CONFIG_DIR; say what the approval menu changes#822
agentforce314 merged 1 commit into
mainfrom
fix/permission-settings-scope

Conversation

@agentforce314

Copy link
Copy Markdown
Owner

Two findings from tracing why a desktop session still asked for approval after Full Access became the default in #819.

The prompt itself was not a bug: Full Access is a floor, and a persisted permissions.defaultMode outranks it by design. The affected machine had {"permissions":{"defaultMode":"default"}} in ~/.clawcodex/settings.json, written by selecting a level in the desktop's approval-mode (⚡) menu.

user_settings_path() ignored the config-dir override

It hardcoded ~/.clawcodex/settings.json, so a config dir relocated with $CLAWCODEX_CONFIG_DIR kept answering permission, hook, trust-boundary, startup-gate and config-health questions from the default home — while that profile's sessions, transcripts and config lived somewhere else. It now resolves through get_user_config_dir() like everything else.

The project tier deliberately does not move: it is scoped to the workspace, and relocating the config home must not start reading a repo's rules from elsewhere. There's a test for that.

The ⚡ menu changed clawcodex everywhere, silently

Selecting a level writes permissions.defaultMode into that same user tier — the one the CLI and TUI read. So one click in a toolbar dropdown became the default for every future session, in every directory, on every surface, with nothing on screen saying so.

Persisting is worth keeping. The backend comment is right that someone who deliberately steps down to "ask every time" should not be silently returned to Full Access next launch — making the menu transient would reintroduce that. The problem is disclosure, not persistence: /permissions in the TUI makes this obvious by being something you deliberately type; a dropdown next to the model chip does not. So the menu now states its scope.

I checked that nothing writes on its own: the renderer's mount effect only calls config.get, the backend never persists a mode at startup (set_settings_default_mode is reached only when persist is set), and the sole writer is the menu's onValueChange.

Testing

  • New tests/test_permission_settings_paths.py — override honored, ~ expanded, home default, project tier unmoved. It binds the real function at import time because conftest.py has an autouse fixture that monkeypatches this path.
  • New menu test asserting the scope note renders.
  • Full Python suite: 9905 passed, 15 skipped. (One unrelated flake, test_bg_bash_completion_notifies_the_model, passes standalone and with its whole file — it is ordering-sensitive in the full run and untouched by this change.)
  • tsc --noEmit clean; locale strings added for en/zh/zh-hant/ja (ar omits this block and falls back).

🤖 Generated with Claude Code

…ay what the approval menu changes

Two findings from tracing why a desktop session still asked for approval
after Full Access became the default.

`user_settings_path()` hardcoded `~/.clawcodex/settings.json`, so a config
dir moved with $CLAWCODEX_CONFIG_DIR kept answering permission, hook, trust
and health questions from the default home while its sessions, transcripts
and config lived elsewhere. It now resolves through `get_user_config_dir()`
like the rest. The project tier stays scoped to the workspace — moving the
config home must not start reading a repo's rules from somewhere else.

The approval-mode (zap) menu writes `permissions.defaultMode` into that same
user tier, which the CLI and TUI read. Persisting is deliberate and worth
keeping: a user who steps DOWN to "ask every time" should not be silently
returned to Full Access on the next launch. But a toolbar dropdown reads
like a local toggle and gave no hint it reaches past the window, so one
click changed clawcodex everywhere, in every directory, invisibly. The menu
now says so. `/permissions` in the TUI already makes this obvious by being
something you type.

Not a code path that fires on its own: the renderer's mount effect only
reads (`config.get`), the backend never persists a mode at startup, and the
only writer is an explicit menu selection.

Co-Authored-By: Claude <noreply@anthropic.com>
@agentforce314
agentforce314 merged commit 981658f into main Aug 9, 2026
3 checks passed
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