From 27e0cb609ffa1ebc5a59c7235b8ca9cde3b554b9 Mon Sep 17 00:00:00 2001 From: Agustin Bettati Date: Wed, 17 Jun 2026 22:32:08 +0200 Subject: [PATCH] feat(ipa): Add IPA-131 declarative tooling extensions catalog with consuming rules in IPA-124 and IPA-111 CLOUDP-412561 --- ipa/general/0111.mdx | 20 ++++++++- ipa/general/0124.mdx | 47 +++++++++++++++++++-- ipa/general/0131.mdx | 97 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 ipa/general/0131.mdx diff --git a/ipa/general/0111.mdx b/ipa/general/0111.mdx index b571d1c..8237e08 100644 --- a/ipa/general/0111.mdx +++ b/ipa/general/0111.mdx @@ -41,6 +41,24 @@ For resources where all fields are server-owned, see ::: +### Optional Fields with Server Defaults + +Assigning a server-side default in place of a client-omitted value during +resource creation is an anti-pattern because it creates hybrid ownership — the +field is client-owned but the server is effectively choosing a value. This makes +it harder for state-driven clients and downstream tooling to tell whether a +returned value was client-set or server-filled. + +- Optional request fields **must** be returned by the server with the same value + (or absence of value) the client provided. +- Legacy fields that retain this anti-pattern **must** be annotated per + [IPA-131](0131.mdx) so declarative tooling can handle the hybrid ownership. +- Booleans are exempt: optional booleans **must** default to `false` per + [Boolean Values](#boolean-values), avoiding a tri-state (true / false / unset) + that is hard for both API producers and consumers to reason about. +- Booleans **must not** be annotated with the extension described in + [IPA-131](0131.mdx). + ### Effective Values There are instances where a service will allocate, generate, or calculate a @@ -73,7 +91,7 @@ the server may scale it up or down based on load. ### Boolean Values -- Booleans **should** default to `false` +- Optional boolean fields **must** default to `false` - Many serialization systems won’t distinguish `false` values from unset values which introduces complications when a boolean defaults to `true` diff --git a/ipa/general/0124.mdx b/ipa/general/0124.mdx index f7c6f16..e2be19b 100644 --- a/ipa/general/0124.mdx +++ b/ipa/general/0124.mdx @@ -17,10 +17,49 @@ reduced when clients need to modify the lists. - A good rule of thumb is 100 elements - If repeated data has the chance of being too large, the API **should** use a sub-resource instead -- [Client-owned](0111.mdx#single-owner-fields) repeated fields **should** be - respected by the server - - The server **should not** modify the order of elements or remove duplicates, - without explicit documentation that the server will do so +- [Client-owned](0111.mdx#single-owner-fields) repeated fields **must** be + respected by the server. + - The server **must not** modify the order of elements or remove duplicates, + unless the field is explicitly declared as a set (see List vs Set below). + +### List vs Set + +Every array property **must** be classified as either a **list** or a **set** so +that clients and downstream tooling can reason correctly about order and +equality. + +- **List** — order is meaningful. Elements may repeat. This is the default + classification when the semantic is not explicitly declared. +- **Set** — order is not meaningful. Elements are unique (no duplicates). + +API producers **must** explicitly declare set-like arrays using the +`x-xgen-array-semantic` extension (see [IPA-131](0131.mdx)) in the OpenAPI spec: + +```yaml +tags: + type: array + x-xgen-array-semantic: set + items: + type: string +``` + +If the extension is omitted, the array is treated as a list. + +#### List (ordered) guarantees + +- For [client-owned](0111.mdx#single-owner-fields) lists, the server **must** + preserve the client-provided order of elements on round-trip (see above). +- For all lists (client-owned or server-owned), the server **must** return + elements in a deterministic, stable order across repeated GET responses for + the same resource state. Unstable ordering causes false drift in declarative + clients. + +#### Set (unordered) guarantees + +- The server **must not** return duplicate elements. +- The server **may** return elements in any order; the order of elements in a + response is not part of the resource state. +- Clients **must** treat equality without regard to order. ## Update Strategy diff --git a/ipa/general/0131.mdx b/ipa/general/0131.mdx new file mode 100644 index 0000000..3ce95b2 --- /dev/null +++ b/ipa/general/0131.mdx @@ -0,0 +1,97 @@ +--- +id: 131 +state: experimental +--- + +# IPA-131: Declarative Tooling Extensions + +Declarative clients such as Infrastructure-as-Code providers consume the OpenAPI +spec to generate typed resources. Some design decisions that affect how a field +should be handled by these clients are not expressible through the standard +OpenAPI schema vocabulary — for example, whether an array is ordered or whether +the server populates an optional field when the client omits it. Encoding these +decisions as OpenAPI extensions under the `x-xgen-*` namespace lets declarative +clients react to them mechanically and removes the need for per-resource manual +overrides. The `x-xgen-*` namespace is specific to MongoDB API tooling. The +registry is intentionally narrow: an extension is added when it expresses a +design decision that declarative clients need to react to mechanically and that +the standard OpenAPI vocabulary cannot express. + +## Guidance + +- Extensions that affect the behavior of declarative or autogenerated clients + **must** live under the `x-xgen-*` prefix. Unregistered `x-xgen-*` keys are + not guaranteed to be honored by downstream tooling. +- Extensions **should not** duplicate information already expressible through + standard OpenAPI keywords (e.g. `readOnly`, `writeOnly`, `default`). They are + intended for cases where existing OpenAPI vocabulary is insufficient to convey + the semantic. + +## Registered Extensions + +### `x-xgen-array-semantic` + +- **Purpose.** Distinguishes arrays whose element order is part of the resource + state (lists) from arrays where it is not (sets). OpenAPI's `type: array` does + not carry this semantic. +- **Referenced by.** [IPA-124 — Repeated Fields](0124.mdx#list-vs-set). +- **Placement.** On any array-typed property. +- **Allowed values.** `list` | `set`. When the extension is absent, the array is + treated as a list. +- **Example:** + + ```yaml + tags: + type: array + x-xgen-array-semantic: set + items: + type: string + ``` + +### `x-xgen-server-computed-when-client-omitted` + +- **Purpose.** Annotate legacy fields that cannot be remediated away from the + optional-plus-server-default pattern, so that declarative tooling can + reconcile the hybrid ownership. Without this marker, tooling sees the + server-assigned value on the response and interprets the field as + client-owned, causing spurious drift. The extension is strictly an escape + hatch for legacy APIs; new APIs are expected to follow one of the clean + ownership patterns documented in IPA-111. +- **Referenced by.** + [IPA-111 — Optional Fields with Server Defaults](0111.mdx#optional-fields-with-server-defaults). +- **Placement.** On any property that exhibits the pattern. **Must not** be + applied to boolean properties — booleans are governed by + [IPA-111 Boolean Values](0111.mdx#boolean-values), which requires optional + booleans to default to `false`. +- **Allowed values.** Boolean. `true` declares the field as subject to the + optional-plus-server-default pattern. Absence of the key is equivalent to + `false`. +- **Example:** + + ```yaml + retentionDays: + type: integer + x-xgen-server-computed-when-client-omitted: true + ``` + +### `x-xgen-server-computed-immutable` + +- **Purpose.** Annotate server-owned fields whose value is computed by the + server at resource creation and does not change on subsequent reads for the + same resource state. The signal allows declarative tooling to treat the value + as stable (safe to cache, no drift polling required) and to omit the field + from Update request bodies. +- **Referenced by.** None — pure tooling hint. The underlying pattern is already + expressible via `readOnly: true`, but `readOnly` alone does not convey + stability across reads. +- **Placement.** On any `readOnly` property that exhibits the pattern. +- **Allowed values.** Boolean. `true` declares the field as server-computed at + creation and stable thereafter. Absence of the key is equivalent to `false`. +- **Example:** + + ```yaml + resourceFingerprint: + type: string + readOnly: true + x-xgen-server-computed-immutable: true + ```