Skip to content

Add managed-config serializer, validation, and per-agent model catalogs - #267

Open
tt-le wants to merge 1 commit into
mainfrom
tien/managed-setup-serializer
Open

Add managed-config serializer, validation, and per-agent model catalogs#267
tt-le wants to merge 1 commit into
mainfrom
tien/managed-setup-serializer

Conversation

@tt-le

@tt-le tt-le commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Changes

The admin-write foundation for the managed CodingAgentConfig, mirroring the developer-read side from #263. No CLI wiring and no network calls: pure functions plus tests. The interactive ucode setup wizard (#PR2) and ucode apply build on this.

managed_setup.py (new)

serialize_managed_config — ucode-native manifest → proto-JSON CodingAgentConfig, the exact inverse of normalize_managed_config. Builds each agent's AgentModelConfig oneof variant, which the server rejects when it doesn't match its agent: claude gets ClaudeDefaultModels family slots, codex gets no model list at all, and opencode/pi/gemini/copilot get a flat list. Output-only fields (workspace_id, timestamps, user ids) are never emitted.

validate_manifest — client-side pre-flight mirroring the handler's validateStoredConfig, so an admin sees problems before the round-trip rather than as an opaque INVALID_PARAMETER_VALUE. Also cross-checks configured models against the workspace's discovered inventory, skipped for agents routing through a Model Provider Service (those ids come from the provider's catalog).

— Per-agent model catalogs: claude → Claude only, gemini → Gemini only, codex → GPT + OSS, opencode/pi/copilot → everything discovered.

~/.ucode/managed-settings.json persistence (0600, workspace-scoped), distinct from managed-state.json: this is the authored manifest, that is the pulled one.

managed_config.py — makes the two enum maps public so the write side inverts them instead of restating them, so a new agent or MCP type is declared once and both directions pick it up. Mechanical rename otherwise.

Note on units

spending_percentage is a fraction in [0, 1], not a percent — the server validates that range. validate_manifest rejects e.g. 80 with a message naming the expected form, since the spec doc's prose says "80%".

Testing

uv run pytest — 1218 passed, 36 skipped on this commit alone. 77 new cases.

The load-bearing one is the round-trip property (serialize#263's normalize == identity), asserted over the full manifest, the minimal manifest, every known agent, and every MCP type. It pins read and write together so they cannot drift as the proto grows — which matters now that #265 consumes this same manifest shape.

This pull request and its description were written by Isaac.

Admin-write foundation for the managed CodingAgentConfig, mirroring the
developer-read side added in the parent commit. No CLI wiring and no network
calls: the interactive `ucode setup` wizard and `ucode apply` build on this in
follow-up changes.

`managed_setup.py`:
- `serialize_managed_config` — ucode-native manifest -> proto-JSON
  `CodingAgentConfig`, the exact inverse of `normalize_managed_config`. Builds
  each agent's `AgentModelConfig` oneof variant, which the server rejects when it
  doesn't match its agent: claude gets `ClaudeDefaultModels` family slots, codex
  gets no model list at all, and opencode/pi/gemini/copilot get a flat list.
  Output-only fields (workspace_id, timestamps, user ids) are never emitted.
- `validate_manifest` — client-side pre-flight mirroring the handler's
  `validateStoredConfig`, so an admin sees problems before the round-trip rather
  than as an opaque INVALID_PARAMETER_VALUE. Also cross-checks configured models
  against the workspace's discovered inventory, skipped for agents routing
  through a Model Provider Service (those ids come from the provider's catalog).
- Per-agent model catalogs: claude -> Claude only, gemini -> Gemini only,
  codex -> GPT + OSS, opencode/pi/copilot -> everything discovered.
- `~/.ucode/managed-settings.json` persistence (0600, workspace-scoped), distinct
  from `managed-state.json`: this is the authored manifest, that is the pulled one.

Note on units: `spending_percentage` is a fraction in [0, 1], not a percent — the
server validates that range. `validate_manifest` rejects e.g. 80 with a message
naming the expected form, since the spec doc's prose says "80%".

`managed_config.py`: makes the two enum maps public so the write side inverts them
instead of restating them — a new agent or MCP type is declared once and both
directions pick it up. Mechanical rename otherwise.

Tests: 77 cases. The load-bearing one is the round-trip property (serialize ->
normalize == identity), asserted over the full manifest, the minimal manifest,
every known agent, and every MCP type — it pins read and write together so they
cannot drift as the proto grows.

Co-authored-by: Isaac
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