Skip to content

feat(api)!: standardize mutation idempotency, results, and structured errors #3051

Description

@drew

User Story

As an API or SDK client, I want mutations to be safely retryable and to return structured outcomes and errors, so that transient failures do not create duplicate resources or require parsing human-readable strings.

Problem Statement

OpenShell mutations do not expose a consistent client request identifier or deduplication contract. Many mutation responses consist of a single boolean such as deleted, attached, or removed, which cannot distinguish accepted asynchronous work, already-absent state, or warnings. Errors generally use gRPC codes and text without standard structured details.

Impact / Why This Matters

Clients cannot safely retry a timed-out create or other non-idempotent mutation because they do not know whether the server committed it. Automation parses error strings for validation details and retry decisions. Boolean acknowledgements leave ambiguous behavior around asynchronous cleanup and partial completion.

Proposed Design

Define one mutation reliability contract:

  • Non-idempotent mutations accept a bounded UUID request_id with documented deduplication lifetime and payload-mismatch behavior.
  • Delete RPCs return google.protobuf.Empty or the affected resource and support allow_missing where idempotent deletion is appropriate.
  • Other one-boolean acknowledgements become empty success responses, the affected resource, or a typed outcome where multiple results are meaningful.
  • Validation, precondition, and retry guidance use standard structured gRPC error details such as BadRequest, ErrorInfo, and RetryInfo.
  • SDKs expose typed errors and retry-safe helpers without requiring string parsing.

Acceptance Criteria

  • Every non-idempotent public mutation is audited and either supports request_id or documents why it is naturally idempotent.
  • Request-ID validation, deduplication lifetime, replay behavior, and mismatched-payload behavior are documented and tested.
  • Single-boolean acknowledgements are removed or justified with an extensible typed outcome.
  • Delete operations have consistent not-found and allow_missing behavior.
  • Validation failures expose field-level structured details.
  • Retryable failures expose machine-readable reason and retry guidance where applicable.
  • All SDKs map structured errors without losing raw gRPC status details.
  • Status-code and retryability tables are published and migration notes cover response-shape changes.

Alternatives Considered

Rely only on server-generated transport request IDs. Those correlate logs but cannot deduplicate client retries. Keep boolean responses and add more fields later. This preserves ambiguity and makes the initial response shape harder to reconcile across SDKs. Adopt a full long-running-operations service. That is substantially larger than the immediate idempotency and result-shape problem.

Agent Investigation

The protobuf tree contains many one-boolean mutation responses and no common client-supplied idempotency field for gateway mutations. HTTP/gRPC request correlation exists but has different semantics. Cleanup acknowledgement behavior in #2999 is a related example.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions