fix(permissions): user settings tier follows $CLAWCODEX_CONFIG_DIR; say what the approval menu changes - #822
Merged
Conversation
…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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.defaultModeoutranks 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 overrideIt hardcoded
~/.clawcodex/settings.json, so a config dir relocated with$CLAWCODEX_CONFIG_DIRkept 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 throughget_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.defaultModeinto 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:
/permissionsin 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_modeis reached only whenpersistis set), and the sole writer is the menu'sonValueChange.Testing
tests/test_permission_settings_paths.py— override honored,~expanded, home default, project tier unmoved. It binds the real function at import time becauseconftest.pyhas an autouse fixture that monkeypatches this path.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 --noEmitclean; locale strings added for en/zh/zh-hant/ja (aromits this block and falls back).🤖 Generated with Claude Code