Skip to content

feat(api)!: split UpdateConfig into typed policy and settings mutations #3049

Open

Description

@drew

User Story

As an API client and operator, I want policy changes and setting changes represented as distinct typed operations, so that each request has unambiguous validation, authorization, and concurrency semantics.

Problem Statement

UpdateConfigRequest combines full policy replacement, incremental policy merge operations, setting upsert, setting deletion, sandbox/global scope, annotations, and optimistic-concurrency fields. Optional fields and action booleans encode mutually exclusive operations only through prose and handler validation. The field named global is also awkward or invalid for normal attribute access in some generated languages.

Impact / Why This Matters

Callers can construct contradictory requests, SDK wrappers must invent their own validation, and the most privileged configuration mutation surface is also the least type-constrained. Policy and settings have different authorization, deletion, versioning, and lifecycle behavior, making one generic response difficult to evolve safely.

Proposed Design

Expose distinct typed operations for policy and setting mutations, with request and response types that carry only fields meaningful to that operation. At minimum, the public contract must separately express:

  • sandbox policy replacement or merge;
  • global policy mutation, if retained;
  • setting upsert; and
  • setting deletion.

Use typed scope selectors rather than global and action booleans. Define optimistic concurrency, annotations, authorization, and returned state for each operation. Internal handlers may share implementation.

Acceptance Criteria

  • No public mutation request relies on unrelated optional fields or action booleans to select the operation.
  • Policy replacement/merge and setting upsert/delete have typed request and response contracts.
  • Sandbox and global scopes are explicit and cannot be combined incorrectly.
  • Authorization requirements are documented and enforced per operation.
  • Optimistic-concurrency behavior and ABORTED responses are consistent and tested.
  • Policy is no longer represented as a magic entry in the settings map.
  • CLI, TUI, all SDKs, protobuf bindings, docs, and migration notes are updated.
  • Old fields and RPCs are removed or deprecated with reserved names/tags as appropriate.

Alternatives Considered

Keep one RPC but replace its fields with a mutation oneof. This would make invalid combinations unrepresentable and may be acceptable if common authorization and response semantics are retained. Separate RPCs are preferred when operations have materially different permissions, concurrency, or results.

Agent Investigation

The current request in proto/openshell.proto contains policy, setting, delete, global-scope, merge-operation, concurrency, annotation, and workspace fields. Provider-backed policy composition in #1988 must remain coherent with the new boundary.

Related: #2565, #1988. Source audit: https://gist.github.com/mrunalp/e80942c1544a0225ee588796a41ab30b.

Activity

  1. github-actions commented on Sep 15, 2026

    @github-actions

    This issue has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. Comment or remove the state:stale label to keep it open.

  2. politerealism commented on Sep 16, 2026

    @politerealism
    Contributor

    Planning to work on this one

  3. politerealism commented on Sep 17, 2026

    @politerealism
    Contributor

    @drew Flagging a timing/tooling interaction before anyone starts implementation here.

    This is a feat(api)!: breaking change to UpdateConfig. RFC 0014 treats the pre-0.1.0 window as the time to make necessary breaking API changes, but the new buf breaking CI gate (#3416, merged) runs buf breaking --against main unconditionally on every PR — it has no concept of a 0.1.0 release boundary, so it will fail on this change's PR the same way before or after 0.1.0 ships.

    Given 0.1.0 is expected to ship very soon, worth deciding explicitly:

    1. Is this meant to land before the 0.1.0 cut? If so, the gate needs a deliberate, reviewed bypass mechanism (it currently has none), since this can't merge cleanly otherwise.
    2. Or does this land after 0.1.0, as a documented Experimental/Stable breaking change following RFC 0014's normal minor-release process (no gate changes needed)?

    No implementation started yet pending this call.

  4. github-actions commented on Oct 2, 2026

    @github-actions

    This issue has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. Comment or remove the state:stale label to keep it open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is neededstate:staleInactive item at risk of automatic closure.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions