From 77372fb93e2d757dbef86d88560d03687a6d5d38 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 16:16:04 +0000 Subject: [PATCH 1/3] feat(spec): retire BATCH_PARTIAL_FAILURE, BATCH_COMPLETE_FAILURE, TRANSACTION_FAILED from StandardErrorCode ADR-0049 enforce-or-remove applied to the error vocabulary, per maintainer ruling on #9266 (2026-08-18): no producer has ever emitted any of the three; the batch surface reports these conditions per row via ledger-registered ROLLED_BACK / NOT_ATTEMPTED at HTTP 200. Removes the 3 enum members, the 3 catalog entries (+ count 53 -> 50), the 3 unpinned-baseline rows, swaps the two test fixture uses to surviving codes, adds a retirement refusal pin, an ADR-0112 amendment, and the ADR-0087 semantic notification entry. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016D9wdJR14KKCxz1WgdAzcw --- .changeset/retire-batch-error-codes.md | 9 +++++ content/docs/api/error-catalog.mdx | 21 +---------- content/docs/references/api/contract.mdx | 5 +-- .../docs/references/api/error-code-ledger.mdx | 8 +--- content/docs/references/api/errors.mdx | 8 +--- .../0112-error-code-vocabulary-and-ledger.md | 4 ++ packages/spec/src/api/batch.test.ts | 4 +- packages/spec/src/api/errors.test.ts | 11 ++++-- packages/spec/src/api/errors.zod.ts | 10 ++--- ...andard-error-code-batch-members-retired.ts | 37 +++++++++++++++++++ packages/spec/src/migrations/registry.ts | 33 +++++++++++++++++ scripts/error-status-unpinned-baseline.json | 5 +-- 12 files changed, 103 insertions(+), 52 deletions(-) create mode 100644 .changeset/retire-batch-error-codes.md create mode 100644 packages/spec/src/migrations/entries/semantic/18.standard-error-code-batch-members-retired.ts diff --git a/.changeset/retire-batch-error-codes.md b/.changeset/retire-batch-error-codes.md new file mode 100644 index 0000000000..7e11812086 --- /dev/null +++ b/.changeset/retire-batch-error-codes.md @@ -0,0 +1,9 @@ +--- +'@objectstack/spec': minor +--- + +Retire `BATCH_PARTIAL_FAILURE`, `BATCH_COMPLETE_FAILURE` and `TRANSACTION_FAILED` from `StandardErrorCode` (ADR-0112 amendment 2026-08-18, ADR-0049 enforce-or-remove, #9266). Breaking for the error vocabulary: the three spellings now fail `StandardErrorCode` / `ApiErrorSchema` parse. No producer has ever emitted any of them — the batch surface reports these conditions per row instead, with strictly more information. + +FROM → TO: `error.code === 'BATCH_PARTIAL_FAILURE' | 'BATCH_COMPLETE_FAILURE' | 'TRANSACTION_FAILED'` (envelope-level, never emitted) → read the per-row `results[].errors[].code` — a rolled-back atomic batch marks each row `ROLLED_BACK`, rows the abort never reached `NOT_ATTEMPTED`, and the causal row keeps its own error (HTTP 200, both codes ledger-registered). One-line fix: delete any branch on the three retired spellings (it never fired) and branch on the per-row codes instead. + + diff --git a/content/docs/api/error-catalog.mdx b/content/docs/api/error-catalog.mdx index 894b471a0d..de607632f4 100644 --- a/content/docs/api/error-catalog.mdx +++ b/content/docs/api/error-catalog.mdx @@ -5,7 +5,7 @@ description: Complete reference for all ObjectStack error codes with causes, fix # Error Code Catalog -ObjectStack uses a structured error system with **9 error categories** and **53 standardized error codes**. Every error includes a machine-readable code, HTTP status mapping, and retry guidance. +ObjectStack uses a structured error system with **9 error categories** and **50 standardized error codes**. Every error includes a machine-readable code, HTTP status mapping, and retry guidance. **Source:** `packages/spec/src/api/errors.zod.ts` @@ -460,25 +460,6 @@ runtime can actually emit. --- -## Batch Operation Errors - -### `BATCH_PARTIAL_FAILURE` -**Cause:** Some operations in a batch request succeeded while others failed. -**Fix:** Check the `details` for individual operation results. Retry only the failed operations. -**Retry:** `retry_immediate` (failed operations only) - -### `BATCH_COMPLETE_FAILURE` -**Cause:** All operations in the batch request failed. -**Fix:** Check the `details` for root cause. Fix and retry the entire batch. -**Retry:** `retry_backoff` - -### `TRANSACTION_FAILED` -**Cause:** A database transaction failed and was rolled back. -**Fix:** Check the `details` for the specific failure. Retry the entire transaction. -**Retry:** `retry_backoff` - ---- - ## Action Errors (`/api/v1/actions`) Since #3962 `/actions` failures speak HTTP like every other route — the status diff --git a/content/docs/references/api/contract.mdx b/content/docs/references/api/contract.mdx index 2611dc36ba..b75be3005e 100644 --- a/content/docs/references/api/contract.mdx +++ b/content/docs/references/api/contract.mdx @@ -27,7 +27,7 @@ const result = ApiErrorSchema.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | -| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +286 more>` | ✅ | Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) | +| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +283 more>` | ✅ | Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) | | **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112, #9106) | | **message** | `string` | ✅ | Readable error message | | **category** | `string` | optional | Error category (e.g. validation, authorization) | @@ -87,9 +87,6 @@ const result = ApiErrorSchema.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` * `ACCOUNT_LOCKED` * `ALREADY_REVERTED` * `AMBIGUOUS_MATCH` diff --git a/content/docs/references/api/error-code-ledger.mdx b/content/docs/references/api/error-code-ledger.mdx index b4a2675b32..bad1569af2 100644 --- a/content/docs/references/api/error-code-ledger.mdx +++ b/content/docs/references/api/error-code-ledger.mdx @@ -192,9 +192,6 @@ const result = ErrorCode.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` * `ACCOUNT_LOCKED` * `ALREADY_REVERTED` * `AMBIGUOUS_MATCH` @@ -446,7 +443,7 @@ const result = ErrorCode.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | | **code** | `string` | ✅ | The registered extension code the waiver keeps admissible | -| **shadows** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +46 more>` | ✅ | The standard-catalog member whose condition the code re-spells | +| **shadows** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +43 more>` | ✅ | The standard-catalog member whose condition the code re-spells | | **reason** | `string` | ✅ | Why the synonym stays registered — recorded so admission is a decision, not drift | ### Allowed Values: `StandardSynonymWaiver.shadows` @@ -501,9 +498,6 @@ const result = ErrorCode.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` --- diff --git a/content/docs/references/api/errors.mdx b/content/docs/references/api/errors.mdx index 2b362260b1..51ef3baaa9 100644 --- a/content/docs/references/api/errors.mdx +++ b/content/docs/references/api/errors.mdx @@ -40,7 +40,7 @@ const result = EnhancedApiErrorSchema.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | -| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +46 more>` | ✅ | Machine-readable error code | +| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +43 more>` | ✅ | Machine-readable error code | | **message** | `string` | ✅ | Human-readable error message | | **category** | `Enum<'validation' \| 'authentication' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'server' \| 'external' \| 'maintenance'>` | optional | Error category | | **httpStatus** | `number` | optional | HTTP status code | @@ -108,9 +108,6 @@ const result = EnhancedApiErrorSchema.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` --- @@ -294,9 +291,6 @@ const result = EnhancedApiErrorSchema.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` --- diff --git a/docs/adr/0112-error-code-vocabulary-and-ledger.md b/docs/adr/0112-error-code-vocabulary-and-ledger.md index efba286774..15cc3ea584 100644 --- a/docs/adr/0112-error-code-vocabulary-and-ledger.md +++ b/docs/adr/0112-error-code-vocabulary-and-ledger.md @@ -103,6 +103,10 @@ Nine rulings, D1–D9. **D2 — `StandardErrorCode` members are renamed in place (breaking).** Same 51 concepts, SCREAMING spelling (`permission_denied` → `PERMISSION_DENIED`). The enum remains the small, closed **standard catalog**: codes with platform-wide HTTP semantics (the `ErrorCategory`/status mapping). It does not try to swallow service-specific codes. `@objectstack/client` re-exports the type (`client/src/index.ts:12`) and uses it in `StandardError` (`:243`) — the client updates in the same PR. +> **Amendment (2026-08-18, [#9266](https://github.com/objectstack-ai/objectstack/issues/9266)) — the three batch-operation members are retired: `BATCH_PARTIAL_FAILURE`, `BATCH_COMPLETE_FAILURE`, `TRANSACTION_FAILED` leave the standard catalog.** The catalog's warrant (D2) is "codes with platform-wide HTTP semantics"; these three never earned it. Measured on 2026-08-18 (#9266's dev report is the evidence of record): no producer has ever emitted any of them — outside the enum declaration the only occurrences in the whole repo were two spec tests using them as arbitrary fixture strings, and `git log -S` over `packages/` shows they have never had a producer since this ADR introduced the vocabulary. The batch write path reports exactly these conditions with **more** information than an envelope code could carry: a rolled-back atomic batch marks each row `errors[0].code = ROLLED_BACK`, rows the abort never reached `NOT_ATTEMPTED`, and the causal row keeps its own error — both codes ledger-registered (D3), both answered at HTTP 200, which also means no doc-publishable HTTP status ever existed for the three (the `check:error-status-conformance` census that found them ungraded is what filed the card). +> +> **Ruled (maintainer, 2026-08-18): 「9266 同意 A」** — ADR-0049 enforce-or-remove applied to the error vocabulary itself. A catalog member no producer can speak is a declared-but-unenforced surface in the one contract every consumer branches on: it teaches an AI author to write `if (error.code === 'BATCH_PARTIAL_FAILURE')`, a branch that can never fire. After removal that spelling fails `ApiErrorSchema` parse at the vocabulary boundary — loud at authoring time, the posture this ADR exists to defend. Accept-set narrowing is acknowledged: a previously-parseable code becomes a parse refusal; the ADR-0087 notification channel is the `standard-error-code-batch-members-retired` semantic entry (no stored-metadata rewrite exists — error codes live on the wire, not in `sys_metadata`). The count claims elsewhere in this ADR ("51 concepts", "53") are historical readings at their own dates and are deliberately not rewritten. + **D3 — Service extension codes are registered, not free-typed.** Codes like `ATTACHMENT_DOWNLOAD_DENIED` or `UPLOAD_SESSION_NOT_FOUND` do not enter the standard catalog. Each service registers its codes in an **error-code ledger** (ADR-0060 pattern; same generation machinery as `api-surface.json` / `json-schema.manifest.json`). Registration carries the code, the owning service, and a one-line meaning. A recommended (not required) convention is a domain prefix (`ATTACHMENT_*`, `UPLOAD_*`). The ledger is the anti-bottleneck: adding a service code touches the service's own ledger entry, not the spec enum — but it is still a *deliberate, reviewable* act, which is the entropy gate this whole problem lacked. **D4 — `ApiErrorSchema.code` stops being `z.string()`.** It becomes the generated union of the standard catalog and the registered ledger (`ErrorCode`, generated at build time — Zod-first per PD#1, enum generated from ledger + `StandardErrorCode`). Conformance suites thereby assert values for free. There is **no regex escape hatch** (see Alternatives — a casing regex passes hallucinated codes, which is precisely the AI failure mode this ADR exists to prevent). diff --git a/packages/spec/src/api/batch.test.ts b/packages/spec/src/api/batch.test.ts index 83e6c4fb1f..fdc4ac3974 100644 --- a/packages/spec/src/api/batch.test.ts +++ b/packages/spec/src/api/batch.test.ts @@ -323,13 +323,13 @@ describe('BatchUpdateResponseSchema', () => { }, ], error: { - code: 'BATCH_PARTIAL_FAILURE', + code: 'INTERNAL_ERROR', message: 'Batch operation failed', }, }); expect(response.failed).toBe(1); - expect(response.error?.code).toBe('BATCH_PARTIAL_FAILURE'); + expect(response.error?.code).toBe('INTERNAL_ERROR'); }); }); diff --git a/packages/spec/src/api/errors.test.ts b/packages/spec/src/api/errors.test.ts index f77bc95146..abd81a042b 100644 --- a/packages/spec/src/api/errors.test.ts +++ b/packages/spec/src/api/errors.test.ts @@ -42,9 +42,14 @@ describe('StandardErrorCode', () => { expect(StandardErrorCode.parse('INSUFFICIENT_PRIVILEGES')).toBe('INSUFFICIENT_PRIVILEGES'); }); - it('should accept batch operation error codes', () => { - expect(StandardErrorCode.parse('BATCH_PARTIAL_FAILURE')).toBe('BATCH_PARTIAL_FAILURE'); - expect(StandardErrorCode.parse('TRANSACTION_FAILED')).toBe('TRANSACTION_FAILED'); + it('refuses the retired batch-operation codes (ADR-0112 amendment 2026-08-18, #9266)', () => { + // Retired under ADR-0049 enforce-or-remove: no producer ever emitted them; + // the batch surface reports these conditions per row via the ledger-registered + // ROLLED_BACK / NOT_ATTEMPTED codes instead. The wrong spelling must fail at + // the vocabulary boundary rather than compile into a branch that never fires. + expect(StandardErrorCode.safeParse('BATCH_PARTIAL_FAILURE').success).toBe(false); + expect(StandardErrorCode.safeParse('BATCH_COMPLETE_FAILURE').success).toBe(false); + expect(StandardErrorCode.safeParse('TRANSACTION_FAILED').success).toBe(false); }); }); diff --git a/packages/spec/src/api/errors.zod.ts b/packages/spec/src/api/errors.zod.ts index 7da0dffcf3..130ab6d963 100644 --- a/packages/spec/src/api/errors.zod.ts +++ b/packages/spec/src/api/errors.zod.ts @@ -117,12 +117,12 @@ export const StandardErrorCode = z.enum([ 'EXTERNAL_SERVICE_ERROR', // External API call failed 'INTEGRATION_ERROR', // Integration service error 'WEBHOOK_DELIVERY_FAILED', // Webhook delivery failed - - // Batch Operation Errors - 'BATCH_PARTIAL_FAILURE', // Batch operation partially succeeded - 'BATCH_COMPLETE_FAILURE', // Batch operation completely failed - 'TRANSACTION_FAILED', // Transaction rolled back ]); +// Retired (ADR-0112 amendment 2026-08-18, ADR-0049 enforce-or-remove, #9266): +// BATCH_PARTIAL_FAILURE / BATCH_COMPLETE_FAILURE / TRANSACTION_FAILED — never +// emitted by any producer in the repo's history; the batch surface reports these +// conditions per row via the ledger-registered ROLLED_BACK / NOT_ATTEMPTED at +// HTTP 200 instead of an envelope-level code. export type StandardErrorCode = z.input; diff --git a/packages/spec/src/migrations/entries/semantic/18.standard-error-code-batch-members-retired.ts b/packages/spec/src/migrations/entries/semantic/18.standard-error-code-batch-members-retired.ts new file mode 100644 index 0000000000..9093508f61 --- /dev/null +++ b/packages/spec/src/migrations/entries/semantic/18.standard-error-code-batch-members-retired.ts @@ -0,0 +1,37 @@ +// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license. + +import type { SemanticMigration } from '../../types.js'; + +export const entry: SemanticMigration = { + id: 'standard-error-code-batch-members-retired', + surface: + '`error.code` values `BATCH_PARTIAL_FAILURE`, `BATCH_COMPLETE_FAILURE` and ' + + '`TRANSACTION_FAILED` — three `StandardErrorCode` members retired from the closed ' + + 'catalog (ADR-0112 amendment 2026-08-18), so constructing or parsing an ApiError ' + + 'with any of them now refuses at the vocabulary boundary', + replacement: + 'branch on the codes the batch surface actually speaks: a rolled-back atomic batch ' + + 'marks each row `errors[0].code = ROLLED_BACK`, rows the abort never reached ' + + '`NOT_ATTEMPTED`, and the causal row keeps its own error — all per row, at HTTP ' + + '200, both codes ledger-registered. Delete any branch on the three retired ' + + 'spellings outright: it never fired, because nothing ever emitted them', + reason: + 'ADR-0049 enforce-or-remove applied to the error vocabulary. No producer has ever ' + + 'emitted any of the three — measured on #9266: outside the enum declaration the ' + + 'only occurrences in the whole repo were two spec tests using them as arbitrary ' + + 'fixture strings, and `git log -S` shows they never had a producer since ADR-0112 ' + + 'introduced the vocabulary. A catalog member no producer can speak teaches an AI ' + + 'author a branch that can never fire; after removal the wrong spelling fails ' + + 'parse at authoring time instead. This is a WIRE vocabulary, not stored metadata ' + + '— no `sys_metadata` row exists for the D2 chain to rewrite, so (like ' + + '`driver-sql-upsert-cross-row-identity-merge-refused`) this entry is the ' + + 'notification channel. No mechanical rewrite exists: a dead branch has no ' + + 'correct mechanical target — the per-row codes carry strictly more information ' + + 'than the envelope code the branch expected. Maintainer ruling 2026-08-18: ' + + '「9266 同意 A」. #9266, ADR-0112, ADR-0049.', + acceptanceCriteria: + 'No consumer branches on the three retired spellings; batch failure handling reads ' + + 'the per-row `results[].errors[].code` (`ROLLED_BACK` / `NOT_ATTEMPTED`) instead ' + + 'of an envelope-level code; constructing an ApiError with a retired spelling ' + + 'fails `StandardErrorCode`/`ApiErrorSchema` parse rather than passing silently.', +}; diff --git a/packages/spec/src/migrations/registry.ts b/packages/spec/src/migrations/registry.ts index c7a157620a..fdeb9d15bd 100644 --- a/packages/spec/src/migrations/registry.ts +++ b/packages/spec/src/migrations/registry.ts @@ -5799,6 +5799,39 @@ const step18: MigrationStep = { + 'the curated retirements answer with their prescriptions. `os validate` exits non-zero ' + 'on a stack carrying any undeclared top-level key, with or without `--strict`.', }, + { + id: 'standard-error-code-batch-members-retired', + surface: + '`error.code` values `BATCH_PARTIAL_FAILURE`, `BATCH_COMPLETE_FAILURE` and ' + + '`TRANSACTION_FAILED` — three `StandardErrorCode` members retired from the closed ' + + 'catalog (ADR-0112 amendment 2026-08-18), so constructing or parsing an ApiError ' + + 'with any of them now refuses at the vocabulary boundary', + replacement: + 'branch on the codes the batch surface actually speaks: a rolled-back atomic batch ' + + 'marks each row `errors[0].code = ROLLED_BACK`, rows the abort never reached ' + + '`NOT_ATTEMPTED`, and the causal row keeps its own error — all per row, at HTTP ' + + '200, both codes ledger-registered. Delete any branch on the three retired ' + + 'spellings outright: it never fired, because nothing ever emitted them', + reason: + 'ADR-0049 enforce-or-remove applied to the error vocabulary. No producer has ever ' + + 'emitted any of the three — measured on #9266: outside the enum declaration the ' + + 'only occurrences in the whole repo were two spec tests using them as arbitrary ' + + 'fixture strings, and `git log -S` shows they never had a producer since ADR-0112 ' + + 'introduced the vocabulary. A catalog member no producer can speak teaches an AI ' + + 'author a branch that can never fire; after removal the wrong spelling fails ' + + 'parse at authoring time instead. This is a WIRE vocabulary, not stored metadata ' + + '— no `sys_metadata` row exists for the D2 chain to rewrite, so (like ' + + '`driver-sql-upsert-cross-row-identity-merge-refused`) this entry is the ' + + 'notification channel. No mechanical rewrite exists: a dead branch has no ' + + 'correct mechanical target — the per-row codes carry strictly more information ' + + 'than the envelope code the branch expected. Maintainer ruling 2026-08-18: ' + + '「9266 同意 A」. #9266, ADR-0112, ADR-0049.', + acceptanceCriteria: + 'No consumer branches on the three retired spellings; batch failure handling reads ' + + 'the per-row `results[].errors[].code` (`ROLLED_BACK` / `NOT_ATTEMPTED`) instead ' + + 'of an envelope-level code; constructing an ApiError with a retired spelling ' + + 'fails `StandardErrorCode`/`ApiErrorSchema` parse rather than passing silently.', + }, { id: 'ui-record-blocks-unknown-keys-refused', surface: 'page `record:alert` / `record:quick_actions` / `record:history` / ' diff --git a/scripts/error-status-unpinned-baseline.json b/scripts/error-status-unpinned-baseline.json index e8f325266a..3683ae21ab 100644 --- a/scripts/error-status-unpinned-baseline.json +++ b/scripts/error-status-unpinned-baseline.json @@ -1,8 +1,6 @@ { - "note": "StandardErrorCode members documented with an HTTP status that NO producer this gate can read declares a status for — nothing pins the doc claim on either side. Shrink-only: a new entry is a gate failure, and a row that becomes pinned must be removed. Regenerate with `node scripts/check-error-status-conformance.mjs --update`.", + "note": "StandardErrorCode members documented with an HTTP status that NO producer this gate can read declares a status for \u2014 nothing pins the doc claim on either side. Shrink-only: a new entry is a gate failure, and a row that becomes pinned must be removed. Regenerate with `node scripts/check-error-status-conformance.mjs --update`.", "unpinned": [ - "BATCH_COMPLETE_FAILURE", - "BATCH_PARTIAL_FAILURE", "CONCURRENT_LIMIT_EXCEEDED", "CONCURRENT_MODIFICATION", "DELETE_RESTRICTED", @@ -33,7 +31,6 @@ "RECORD_NOT_ACCESSIBLE", "SESSION_EXPIRED", "TIME_RESTRICTED", - "TRANSACTION_FAILED", "VALUE_OUT_OF_RANGE", "VALUE_TOO_LONG", "VALUE_TOO_SHORT", From 050dabd0caa9b648e913c0238d142d831f7b94a2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 20:03:44 +0000 Subject: [PATCH 2/3] merge origin/main (os-regen artifacts taken from main; regeneration follows) --- content/docs/references/api/contract.mdx | 5 ++++- content/docs/references/api/error-code-ledger.mdx | 8 +++++++- content/docs/references/api/errors.mdx | 8 +++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/content/docs/references/api/contract.mdx b/content/docs/references/api/contract.mdx index b75be3005e..2611dc36ba 100644 --- a/content/docs/references/api/contract.mdx +++ b/content/docs/references/api/contract.mdx @@ -27,7 +27,7 @@ const result = ApiErrorSchema.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | -| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +283 more>` | ✅ | Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) | +| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +286 more>` | ✅ | Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) | | **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112, #9106) | | **message** | `string` | ✅ | Readable error message | | **category** | `string` | optional | Error category (e.g. validation, authorization) | @@ -87,6 +87,9 @@ const result = ApiErrorSchema.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` +* `BATCH_PARTIAL_FAILURE` +* `BATCH_COMPLETE_FAILURE` +* `TRANSACTION_FAILED` * `ACCOUNT_LOCKED` * `ALREADY_REVERTED` * `AMBIGUOUS_MATCH` diff --git a/content/docs/references/api/error-code-ledger.mdx b/content/docs/references/api/error-code-ledger.mdx index bad1569af2..b4a2675b32 100644 --- a/content/docs/references/api/error-code-ledger.mdx +++ b/content/docs/references/api/error-code-ledger.mdx @@ -192,6 +192,9 @@ const result = ErrorCode.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` +* `BATCH_PARTIAL_FAILURE` +* `BATCH_COMPLETE_FAILURE` +* `TRANSACTION_FAILED` * `ACCOUNT_LOCKED` * `ALREADY_REVERTED` * `AMBIGUOUS_MATCH` @@ -443,7 +446,7 @@ const result = ErrorCode.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | | **code** | `string` | ✅ | The registered extension code the waiver keeps admissible | -| **shadows** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +43 more>` | ✅ | The standard-catalog member whose condition the code re-spells | +| **shadows** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +46 more>` | ✅ | The standard-catalog member whose condition the code re-spells | | **reason** | `string` | ✅ | Why the synonym stays registered — recorded so admission is a decision, not drift | ### Allowed Values: `StandardSynonymWaiver.shadows` @@ -498,6 +501,9 @@ const result = ErrorCode.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` +* `BATCH_PARTIAL_FAILURE` +* `BATCH_COMPLETE_FAILURE` +* `TRANSACTION_FAILED` --- diff --git a/content/docs/references/api/errors.mdx b/content/docs/references/api/errors.mdx index 51ef3baaa9..2b362260b1 100644 --- a/content/docs/references/api/errors.mdx +++ b/content/docs/references/api/errors.mdx @@ -40,7 +40,7 @@ const result = EnhancedApiErrorSchema.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | -| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +43 more>` | ✅ | Machine-readable error code | +| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +46 more>` | ✅ | Machine-readable error code | | **message** | `string` | ✅ | Human-readable error message | | **category** | `Enum<'validation' \| 'authentication' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'server' \| 'external' \| 'maintenance'>` | optional | Error category | | **httpStatus** | `number` | optional | HTTP status code | @@ -108,6 +108,9 @@ const result = EnhancedApiErrorSchema.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` +* `BATCH_PARTIAL_FAILURE` +* `BATCH_COMPLETE_FAILURE` +* `TRANSACTION_FAILED` --- @@ -291,6 +294,9 @@ const result = EnhancedApiErrorSchema.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` +* `BATCH_PARTIAL_FAILURE` +* `BATCH_COMPLETE_FAILURE` +* `TRANSACTION_FAILED` --- From ad9ec0702180ccb9cf30f00c7ef7bd7e6232a762 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Aug 2026 20:11:41 +0000 Subject: [PATCH 3/3] chore(spec): regenerate references docs on merged tree (post-merge regeneration) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_016D9wdJR14KKCxz1WgdAzcw --- content/docs/references/api/contract.mdx | 5 +---- content/docs/references/api/error-code-ledger.mdx | 8 +------- content/docs/references/api/errors.mdx | 8 +------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/content/docs/references/api/contract.mdx b/content/docs/references/api/contract.mdx index 2611dc36ba..b75be3005e 100644 --- a/content/docs/references/api/contract.mdx +++ b/content/docs/references/api/contract.mdx @@ -27,7 +27,7 @@ const result = ApiErrorSchema.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | -| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +286 more>` | ✅ | Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) | +| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +283 more>` | ✅ | Error code (e.g. VALIDATION_ERROR; StandardErrorCode ∪ the ledger the serving side registers — ERROR_CODE_LEDGER for framework packages) | | **declaredCode** | `string` | optional | The producer-declared code, verbatim, when it is not a member of the closed `code` vocabulary — the open, author-authored channel (app-specific spellings; ADR-0112, #9106) | | **message** | `string` | ✅ | Readable error message | | **category** | `string` | optional | Error category (e.g. validation, authorization) | @@ -87,9 +87,6 @@ const result = ApiErrorSchema.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` * `ACCOUNT_LOCKED` * `ALREADY_REVERTED` * `AMBIGUOUS_MATCH` diff --git a/content/docs/references/api/error-code-ledger.mdx b/content/docs/references/api/error-code-ledger.mdx index b4a2675b32..bad1569af2 100644 --- a/content/docs/references/api/error-code-ledger.mdx +++ b/content/docs/references/api/error-code-ledger.mdx @@ -192,9 +192,6 @@ const result = ErrorCode.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` * `ACCOUNT_LOCKED` * `ALREADY_REVERTED` * `AMBIGUOUS_MATCH` @@ -446,7 +443,7 @@ const result = ErrorCode.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | | **code** | `string` | ✅ | The registered extension code the waiver keeps admissible | -| **shadows** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +46 more>` | ✅ | The standard-catalog member whose condition the code re-spells | +| **shadows** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +43 more>` | ✅ | The standard-catalog member whose condition the code re-spells | | **reason** | `string` | ✅ | Why the synonym stays registered — recorded so admission is a decision, not drift | ### Allowed Values: `StandardSynonymWaiver.shadows` @@ -501,9 +498,6 @@ const result = ErrorCode.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` --- diff --git a/content/docs/references/api/errors.mdx b/content/docs/references/api/errors.mdx index 2b362260b1..51ef3baaa9 100644 --- a/content/docs/references/api/errors.mdx +++ b/content/docs/references/api/errors.mdx @@ -40,7 +40,7 @@ const result = EnhancedApiErrorSchema.parse(data); | Property | Type | Required | Description | | :--- | :--- | :--- | :--- | -| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +46 more>` | ✅ | Machine-readable error code | +| **code** | `Enum<'VALIDATION_ERROR' \| 'INVALID_FIELD' \| 'MISSING_REQUIRED_FIELD' \| 'INVALID_FORMAT' \| 'VALUE_TOO_LONG' \| 'VALUE_TOO_SHORT' \| 'VALUE_OUT_OF_RANGE' \| … +43 more>` | ✅ | Machine-readable error code | | **message** | `string` | ✅ | Human-readable error message | | **category** | `Enum<'validation' \| 'authentication' \| 'authorization' \| 'not_found' \| 'conflict' \| 'rate_limit' \| 'server' \| 'external' \| 'maintenance'>` | optional | Error category | | **httpStatus** | `number` | optional | HTTP status code | @@ -108,9 +108,6 @@ const result = EnhancedApiErrorSchema.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` --- @@ -294,9 +291,6 @@ const result = EnhancedApiErrorSchema.parse(data); * `EXTERNAL_SERVICE_ERROR` * `INTEGRATION_ERROR` * `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` ---