Resolve managed coding-agent config over local state at launch - #265
Open
asujithan wants to merge 2 commits into
Open
Resolve managed coding-agent config over local state at launch#265asujithan wants to merge 2 commits into
asujithan wants to merge 2 commits into
Conversation
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.
Changes
Applies the admin-published managed coding-agent config at launch, taking precedence over the developer's own ucode settings.
On every ucode launch, the workspace's managed config is re-read and persisted to ~/.ucode/managed-state.json, then resolved against ~/.ucode/state.json per key — the manifest wins, the developer's state fills any gaps. The resolved view renders the agent settings file (e.g. ~/.claude/ucode-settings.json); the two state files are never merged on disk, so state.json keeps recording what the developer configured for themselves.
— managed_resolve.py — pure precedence logic. Claude resolves per model family (a family the admin didn't pin keeps the developer's choice); flat-list agents take the manifest's list wholesale.
— managed_config.py — adds the per-launch refresh. A failed read falls back to the last persisted config rather than blocking the launch; a successful "no config" read clears the persisted copy so a removed policy can't reappear after an outage.
— state.py — save_state swaps managed values back for the developer's own before writing, so precedence never overwrites state.json.
— cli.py — wires this into _launch_tool. An explicit --provider that conflicts with the admin's is a hard error; a managed provider that can't be used names the managed config in the error.
Followups:
Test plan
uv run pytest — 1173 passed, 6 skipped. ruff check . and ruff format --check . clean.
tests/test_managed_resolve.py
— TestClaudeModels (5) — proto slots → families; manifest wins per family; unpinned family keeps developer's value; falls back to local; None when neither side has models
— TestListModels (3) — manifest list replaces local; local stands when manifest silent; blank entries dropped
— TestManagedProviderService (3) — manifest-only read, no fallback to local state
— TestResolveState (4) — input state not mutated; managed values layered onto the copy; unrelated keys preserved; other tools' providers untouched
— TestStateFileIsNotRewritten (8) — the core contract, run against a real temp state.json with save_state unstubbed: developer's model survives; settings file still gets the managed model; overlay bookkeeping never on disk; repeated saves stay idempotent (the relayed-proxy path saves twice); provider_services preserved; a developer with no prior value isn't given one; codex + gemini parametrized
tests/test_managed_config.py
— TestRefreshManagedConfig (9) — persists and returns the manifest; no-config returns None; no-config clears the persisted copy; read failure and auth failure each fall back to persisted, or to local settings when there is none; empty persisted marker isn't treated as a fallback; no workspace is a no-op
— TestManagedLaunchState (2) — layers managed models when a config exists; hands state back untouched when none
— TestPersistence — empty config overwrites a previous one
Ran e2e tests locally against eng-ml-inference-us-east-1 workspace and all tests pass except tests/test_e2e.py::TestCodexLaunch::test_launch_codex_per_model which is expected due to codex model list fix not yet being rolled out to prod.