Skip to content

[AIGTWY] Add ug configure --global/--user scope - #1207

Merged
JTSIV1 merged 2 commits into
mainfrom
jtsiv1/configure-user-scope
Oct 21, 2026
Merged

JTSIV1 merged 2 commits into
mainfrom
jtsiv1/configure-user-scope

Conversation

@JTSIV1

@JTSIV1 JTSIV1 commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Adds ug configure --global / --user so Claude Code and Codex can be configured without sudo.

What

  • --global is the default and behaves exactly as today: ug writes the machine-wide managed settings with sudo.

  • --user skips the machine-wide managed files, the same way UG_SKIP_OS_SETTINGS does, so it needs no sudo. ug relies on what it already passes at launch:

    • Claude Code: --settings ~/.claude/ucode-settings.json
    • Codex: -c overrides from ~/.codex/ucode.config.toml

    Model lists still update without sudo. Managed MCP servers register in user scope. Only ug claude and ug codex launches reach the gateway in this mode.

  • Refusal. ug configure --user refuses, before configuring any agent, while a selected agent's machine-wide managed file exists, whoever wrote it, because managed settings override --settings and -c. The message points to ug revert, the administrator, or --global. On Windows, the Codex check covers the machine-wide requirements.toml. Launches don't check: a managed file added later simply takes precedence.

  • Saved choice. The scope is saved as config_scope in ~/.ucode/state.json after a successful configure and applies to later launches. ug configure without --user and ug revert reset it to global. Passing both flags is an error.

Testing

  • Unit tests:
    • TestConfigureScopeFlags in tests/test_cli.py
    • TestConfigureToolUserScope in tests/test_agents_init.py
    • the user-scope bypass and MCP tests in tests/test_agent_claude.py and tests/test_agent_codex.py
    • scope tests in tests/test_managed_files.py
  • Results: the full suite has the same 8 failures as plain main: TestInstallToolBinary required_update ×4, TestDetachedReporter ×2, and test_skill_toggles_with_launch_installation_despite_shadowed_path ×2. ruff and ty are clean.
  • Manual E2E: ran tests on an arclet to verify intended behavior and interactions of --user and global plus default no flag behavior
  • Integration tests: unchanged here, since --global is the default. A follow-up PR will add a --user integration test.

@JTSIV1
JTSIV1 marked this pull request as ready for review October 20, 2026 14:36
@JTSIV1
JTSIV1 force-pushed the jtsiv1/configure-user-scope branch from 83a2206 to c7fbec3 Compare October 20, 2026 15:24
@billiam-wang
billiam-wang self-requested a review October 20, 2026 22:16
Comment thread src/ucode/agents/__init__.py Outdated
@billiam-wang billiam-wang added the ug-review Run the automated UG review label Oct 21, 2026
@billiam-wang

Copy link
Copy Markdown
Collaborator

/ug-review

Comment thread src/ucode/agents/__init__.py Outdated
Comment thread src/ucode/agents/__init__.py Outdated
Comment thread src/ucode/agents/__init__.py Outdated
Comment thread tests/test_agents_init.py Outdated
@github-actions

Copy link
Copy Markdown

Polly AI Review

Blocking issues

  1. Configure saves user scope after refusing configuration — src/ucode/agents/__init__.py:448, src/ucode/cli.py:4357

    With an existing managed file, ug configure --user --agents claude,codex raises inside configure_tool, but configure_selected_tools catches the refusal and continues. Configure exits successfully and saves config_scope=user, even when neither agent configured. Subsequent launches then refuse.

    Can we propagate scope conflicts before configuration writes or scope persistence? Extend TestConfigureScopeFlags.test_failed_configure_does_not_persist_scope through the real refusal path, including partial success.

Security vulnerabilities

None identified in the reviewed changes.

Non-blocking notes

  • Can we parameterize the Claude/Codex test_skip_env_bypasses_* tests with the new user-scope cases? They repeat the same setup and assertions.
  • Can we move TestConfigureScopeFlags.test_clear_state_resets_scope_to_default into the existing tests/test_state.py::TestClearState scenario?
  • Can we add an offline launch test for saved user scope plus an existing managed file, asserting refusal before agent execution? Current tests exercise configure dispatch, not launch.

Approach

Reusing managed_settings_skipped() and the existing user-scope MCP fallback is consistent with the current design. Scope conflicts need a fatal path distinct from recoverable per-agent configuration failures.

Summary

Diff size (computed): 14 files; +299 / -8 lines (307 changed text lines); 0 binary files.

Category Files Added Deleted Share of changed text lines
Tests and test support 6 190 0 61.9%
Documentation 2 14 0 4.6%
Dependencies and lockfiles 0 0 0 0.0%
Implementation / other 6 95 8 33.6%

Shares use additions + deletions, including generated text and lockfiles. Binary files have no line count. Tests include fixtures/helpers under test paths and colocated test/spec files; categories are path-based, not coverage measurements.

Changes

Adds configure-scope flags, persists the selected scope, skips OS-managed settings in user mode, and rejects conflicting managed files. Supporting changes cover state isolation, adapter behavior, CLI handling, and documentation. No dependencies or generated files change.

The implementation is proportional to the stated outcome. Concrete reductions are limited to overlapping opt-out test setup; no broad scaffolding or unrelated cleanup was found.

Tests

Test-by-test assessment

The frozen diff and relevant existing tests were inspected. An isolated offline control-flow reproduction confirmed that per-agent refusals are swallowed; it was not an exact-head test-suite run. The reported full-suite failures and clean lint/type checks remain author-reported, not independently verified.

Test / case Behavior protected Layer Needed? Action / rationale
tests/conftest.py::_isolate_ucode_state Prevents scope leaking between tests Fixture keep Newly mutable process state requires isolation.
tests/test_agent_claude.py::TestWriteToolConfigManagedSettings.test_user_scope_bypasses_managed_file Writes local Claude settings without privileged writes Component consolidate Parameterize existing test_skip_env_bypasses_managed_file by opt-out mechanism; preserve local-file and no-managed-write assertions.
tests/test_agent_claude.py::TestClaudeReconcileManagedMcp.test_user_scope_returns_false_without_touching_managed_file Declines managed MCP delivery without reading or writing the managed file Unit keep Directly protects the new adapter guard.
tests/test_agent_codex.py::TestCodexManagedConfig.test_user_scope_bypasses_managed_config Writes local Codex configuration without privileged writes Component consolidate Parameterize existing test_skip_env_bypasses_managed_config; the protected behavior and setup match.
tests/test_agent_codex.py::TestCodexReconcileManagedMcp.test_user_scope_returns_false_without_touching_managed_file Declines Codex managed MCP delivery without managed-file access Unit keep Protects the separate Codex adapter guard.
tests/test_agents_init.py::TestConfigureToolUserScope.test_managed_file_blocks_user_scope — Claude/Codex, exists=True Refuses existing managed files before writers run Unit keep Covers both agent path selectors and verifies no configuration write occurs.
tests/test_agents_init.py::TestConfigureToolUserScope.test_managed_file_blocks_user_scope — Claude/Codex, exists=False Allows configuration without a conflicting managed file Unit keep Necessary positive counterpart; checks writer dispatch.
tests/test_agents_init.py::TestConfigureToolUserScope.test_no_managed_path_is_not_checked Allows platforms without a managed path Unit keep None is a distinct boundary from a nonexistent file.
tests/test_cli.py::TestConfigureScopeFlags.test_default_is_global_and_not_persisted Preserves fresh-install defaults CLI component keep Checks compatibility and absence of unnecessary persisted state.
tests/test_cli.py::TestConfigureScopeFlags.test_user_flag_sets_and_persists_scope Activates and saves explicit user scope CLI component keep Protects flag-to-state wiring.
tests/test_cli.py::TestConfigureScopeFlags.test_global_flag_overrides_saved_user_scope Switches saved user scope back to global CLI component keep Covers the explicit reverse transition.
tests/test_cli.py::TestConfigureScopeFlags.test_saved_scope_is_reused_without_flag Reuses saved scope during configure CLI component keep Protects subsequent flagless configuration.
tests/test_cli.py::TestConfigureScopeFlags.test_failed_configure_does_not_persist_scope Avoids persistence after a propagated failure CLI component keep Useful, but mocks away the actual swallowed-refusal path; extend as described in the blocking issue.
tests/test_cli.py::TestConfigureScopeFlags.test_flags_are_mutually_exclusive Rejects conflicting scope flags before configuration CLI component keep Checks the error, lack of configuration calls, and unchanged persistence.
tests/test_cli.py::TestConfigureScopeFlags.test_dry_run_applies_but_does_not_persist Avoids saving dry-run scope CLI component keep Distinct persistence boundary.
tests/test_cli.py::TestConfigureScopeFlags.test_saved_scope_applies_to_other_commands Loads saved scope outside configure CLI component keep Protects root-callback wiring; the help invocation does not establish launch behavior.
tests/test_cli.py::TestConfigureScopeFlags.test_revert_does_not_apply_saved_user_scope Lets revert operate independently of saved user scope CLI component keep Protects the callback exception; mocked revert does not verify cleanup itself.
tests/test_cli.py::TestConfigureScopeFlags.test_clear_state_resets_scope_to_default Removes persisted scope when clearing state State unit move layer Extend tests/test_state.py::TestClearState.test_clears_current_workspace; CLI scaffolding is unnecessary.
tests/test_managed_files.py::TestSkipOsSettings.test_user_scope_skips_managed_settings_and_files Connects user scope to shared managed-settings eligibility Unit keep Smallest reliable layer for the shared gate.
tests/test_managed_files.py::TestSkipOsSettings.test_set_config_scope_rejects_unknown_scope Rejects invalid setter values Unit keep Direct validation of the new state API.

Existing opt-out writer tests, Codex launch-override tests, and MCP fallback tests provide useful baseline coverage. They do not establish the new configure-refusal-to-persistence contract or saved-scope launch refusal. Those gaps can be covered offline at the component layer; a live E2E is not needed for these assertions.

Scope

All changed files support the stated user/global configuration outcome. No clearly unrelated changes were found. The scope-persistence failure is an implementation defect within that outcome, not unrelated work.


Automated review by Polly · workflow run

JTSIV1 and others added 2 commits October 21, 2026 13:47
--user skips the OS-managed Claude Code and Codex settings like UG_SKIP_OS_SETTINGS, so no
sudo is needed and ug relies on the per-user files it already passes at launch. It fails while
an OS-managed file for the agent exists, and the choice is saved after a successful configure.

Co-authored-by: Isaac <no-reply@databricks.com>
… present files

Co-authored-by: Isaac <no-reply@databricks.com>
@JTSIV1
JTSIV1 force-pushed the jtsiv1/configure-user-scope branch from c7fbec3 to 9aa9698 Compare October 21, 2026 14:57
@JTSIV1
JTSIV1 added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 1aaa80d Oct 21, 2026
148 of 155 checks passed
@JTSIV1
JTSIV1 deleted the jtsiv1/configure-user-scope branch October 21, 2026 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ug-review Run the automated UG review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants