From ef3e59108446f7d0db07c44453db847d017de478 Mon Sep 17 00:00:00 2001 From: Albert Wu Date: Tue, 21 Jul 2026 08:35:07 -0700 Subject: [PATCH] docs(rfc): propose shared platform errors Summary: Define stable platform error identities for conditions with common semantics across domains. Keep error identity separate from retry classification, use platform errors directly rather than through domain aliases, and illustrate the distinction with ErrVersionMismatch and ErrNotFound. Test Plan: - make fmt - make lint - make check-tidy - make check-gazelle Revert Plan: Revert this commit. API Changes: N/A Monitoring and Alerts: N/A --- doc/rfc/index.md | 1 + doc/rfc/shared-platform-errors.md | 50 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 doc/rfc/shared-platform-errors.md diff --git a/doc/rfc/index.md b/doc/rfc/index.md index 3f2a9af2..4cb678dc 100644 --- a/doc/rfc/index.md +++ b/doc/rfc/index.md @@ -8,6 +8,7 @@ Design documents and technical proposals, grouped by scope. Shared/cross-cutting - [Message Queue Contract](messagequeue-contract.md) - How queue payloads are defined (Protobuf, serialized as protobuf JSON), located by audience (external in `api/{domain}/messagequeue/`, internal in `{domain}/core/messagequeue/`), bound to topics (the `topics` proto option), and enforced by Bazel visibility - [Consumer Gate](consumer-gate.md) - Stopping and starting individual queue controllers at runtime via consumer middleware: parked deliveries held in-flight with visibility extension, gate state as a separate extension with a file-based first implementation shared by tests and operators - [Change URIs](change-uri.md) - Identity of a code change: `scheme://{host[:port]}/{path}` per provider (GitHub PR, Phabricator Diff, git ref/commit) and canonical-form rules +- [Shared Platform Errors](shared-platform-errors.md) - Cross-domain error identities with common classification only where recovery behavior is universal ## SubmitQueue diff --git a/doc/rfc/shared-platform-errors.md b/doc/rfc/shared-platform-errors.md new file mode 100644 index 00000000..41109d41 --- /dev/null +++ b/doc/rfc/shared-platform-errors.md @@ -0,0 +1,50 @@ +# Shared Platform Errors + +Define shared error identities for conditions that have the same meaning across domains. A shared identity enables consistent `errors.Is` checks and, where appropriate, a common platform classification. Sharing an error identity does not by itself make the error retryable. + +## Problem + +SubmitQueue and Stovepipe define duplicate sentinels for common conditions such as a missing resource and an optimistic version conflict. Callers must know which domain package produced the error, and platform code cannot apply a common policy without importing domain packages or adding domain-specific classifiers. + +| Concern | Current | Proposed | +|---|---|---| +| Error identity | Each domain defines equivalent sentinels | `platform/errs` owns shared semantic sentinels | +| Usage | Callers check domain aliases such as `storage.ErrVersionMismatch` | Callers use `platformerrs.ErrVersionMismatch` directly | +| Retry policy | Classification is repeated in controllers or domain wiring | The platform classifies only errors whose recovery policy is universal | +| Domain contracts | Storage and configuration contracts own both APIs and common error identities | Domain contracts remain domain-owned; only shared error identities move | + +## Proposal + +An error belongs in `platform/errs` when: + +1. It has the same meaning across multiple domains. +2. Callers benefit from one `errors.Is` identity. +3. Its meaning is independent of a domain entity or extension API. +4. Any default classification applied by the platform is valid for every use. + +Domains use shared errors directly rather than re-exporting them through permanent aliases: + +```go +if errors.Is(err, platformerrs.ErrVersionMismatch) { + // Handle workflow-specific convergence. +} +``` + +Implementations preserve context with wrapping: + +```go +return fmt.Errorf("update batch %s: %w", batch.ID, platformerrs.ErrVersionMismatch) +``` + +A domain may define a more specific error that wraps a platform error when callers need both identities. Domain storage and configuration interfaces remain in their existing packages. + +## Initial errors + +| Error | Meaning | Default platform classification | +|---|---|---| +| `ErrVersionMismatch` | An optimistic conditional update lost a concurrent race | `InfraRetryable` | +| `ErrNotFound` | A requested resource does not exist | None; remains non-retryable by default | + +`ErrVersionMismatch` has a universal recovery policy: reload current state and retry or converge. `ErrNotFound` does not. Depending on the call site, absence may be an expected result, a user error, or an infrastructure invariant violation. Controllers may add a contextual classification only when they have information the shared error does not carry. + +The generic classifier recognizes `ErrVersionMismatch` through the existing error-chain walk. This proposal does not change retry limits, backoff, DLQ policy, or delivery guarantees.