From 084972d34a81b23b98b398e989e67c1d720991d1 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 13:34:03 +0000 Subject: [PATCH 1/4] feat(spec): mechanical standard-synonym admission gate on the error-code ledger (#8211) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Option C per the #8211 adjudication (2026-08-12): the ledger header's 'use the standard catalog instead of registering a synonym' rule gets teeth. standardSynonymOf() is a closed two-prong detector (HTTP reason-phrase alias via HttpStatusErrorCodeMap; token-subset of a standard member's name); standardSynonymViolations() is the admission gate's engine; STANDARD_SYNONYM_WAIVERS grandfathers the pre-gate synonyms (CONFLICT, NOT_FOUND, FORBIDDEN, INTERNAL, and UNAUTHORIZED which the detector surfaced) with recorded reasons naming the member each shadows. No wire change; consolidation (option B) stays deferred. The suite pins that the gate rejects a newly-introduced synonym on both prongs — a detector that only passes on today's tree is the failure mode this closes. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012MNV7ZSCjNfA38eDCjsXQL --- .changeset/ledger-synonym-admission-gate.md | 34 +++ packages/spec/authorable-surface/api.json | 3 + packages/spec/json-schema.manifest/api.json | 1 + .../spec/src/api/error-code-ledger.test.ts | 121 +++++++++- .../spec/src/api/error-code-ledger.zod.ts | 222 +++++++++++++++++- .../src/type-alias-convention.pin.test.ts | 20 +- 6 files changed, 396 insertions(+), 5 deletions(-) create mode 100644 .changeset/ledger-synonym-admission-gate.md diff --git a/.changeset/ledger-synonym-admission-gate.md b/.changeset/ledger-synonym-admission-gate.md new file mode 100644 index 0000000000..4fd5285fb2 --- /dev/null +++ b/.changeset/ledger-synonym-admission-gate.md @@ -0,0 +1,34 @@ +--- +"@objectstack/spec": minor +--- + +feat(spec): the error-code ledger's "no synonym of the standard catalog" rule is now mechanical, with recorded waivers (#8211, option C) + +The ledger header has always said: if the condition is generic, use the +standard catalog instead of registering a synonym. That rule was prose only — +the admission gate rejected a code that is *literally* a `StandardErrorCode` +member, so four semantic synonyms (`CONFLICT`, `NOT_FOUND`, `FORBIDDEN`, +`INTERNAL`) accumulated without anyone deciding to allow them. + +The rule now has teeth. New exports on `@objectstack/spec`: + +- `standardSynonymOf(code)` — a **closed, mechanical** detector (no NLP fuzz): + a code is a semantic synonym of a standard member when it is the + SCREAMING_SNAKE spelling of an HTTP reason phrase whose status + `HttpStatusErrorCodeMap` maps to a member (`FORBIDDEN` → 403 → + `PERMISSION_DENIED`), or when every `_`-token of the code appears in a + member's name (`CONFLICT` ⊆ `RESOURCE_CONFLICT`). +- `standardSynonymViolations(ledger, waivers)` — every unwaived synonym + registration; the admission suite asserts it is empty and pins that the same + function rejects a newly-introduced synonym (both prongs). +- `StandardSynonymWaiverSchema` / `StandardSynonymWaiver` / + `STANDARD_SYNONYM_WAIVERS` — a waiver names the member the code shadows and + carries a recorded reason, so admission is a decision on the record, never + drift. Stale waivers fail the suite. + +No wire change: the existing synonyms — the four above plus `UNAUTHORIZED` +(401 reason phrase of `UNAUTHENTICATED`'s condition, surfaced by the detector) +— are grandfathered via explicit waiver entries. Consolidating any of them +onto the member it shadows (option B) is deferred until a specific code has a +measured victim. One rule, two doors: this is the admission-door half of the +family whose dispatcher-door half was ruled in #8087. diff --git a/packages/spec/authorable-surface/api.json b/packages/spec/authorable-surface/api.json index fc2adf9985..6d968574cb 100644 --- a/packages/spec/authorable-surface/api.json +++ b/packages/spec/authorable-surface/api.json @@ -1538,6 +1538,9 @@ "api/SingleRecordResponse:error", "api/SingleRecordResponse:meta", "api/SingleRecordResponse:success", + "api/StandardSynonymWaiver:code", + "api/StandardSynonymWaiver:reason", + "api/StandardSynonymWaiver:shadows", "api/SubscribeMessage:messageId", "api/SubscribeMessage:subscription", "api/SubscribeMessage:timestamp", diff --git a/packages/spec/json-schema.manifest/api.json b/packages/spec/json-schema.manifest/api.json index c86259f937..26b2dcaccd 100644 --- a/packages/spec/json-schema.manifest/api.json +++ b/packages/spec/json-schema.manifest/api.json @@ -363,6 +363,7 @@ "api/SimplePresenceState", "api/SingleRecordResponse", "api/StandardErrorCode", + "api/StandardSynonymWaiver", "api/SubscribeMessage", "api/Subscription", "api/SubscriptionEvent", diff --git a/packages/spec/src/api/error-code-ledger.test.ts b/packages/spec/src/api/error-code-ledger.test.ts index 7878b07b8f..82c5a3adee 100644 --- a/packages/spec/src/api/error-code-ledger.test.ts +++ b/packages/spec/src/api/error-code-ledger.test.ts @@ -2,7 +2,16 @@ import { describe, it, expect } from 'vitest'; import { StandardErrorCode } from './errors.zod'; -import { ERROR_CODE_LEDGER, REGISTERED_ERROR_CODES, ErrorCode } from './error-code-ledger.zod'; +import { + ERROR_CODE_LEDGER, + REGISTERED_ERROR_CODES, + ErrorCode, + STANDARD_SYNONYM_WAIVERS, + StandardSynonymWaiverSchema, + standardSynonymOf, + standardSynonymViolations, + type StandardSynonymWaiver, +} from './error-code-ledger.zod'; /** * Ledger admission rules (ADR-0112 D3). These are the invariants that make a @@ -45,6 +54,116 @@ describe('ERROR_CODE_LEDGER', () => { const union = [...new Set(entries.flatMap(([, codes]) => [...codes]))].sort(); expect([...REGISTERED_ERROR_CODES]).toEqual(union); }); + + it('no registered code is an unwaived semantic synonym of a standard member (#8211)', () => { + // The ledger header's "use the standard catalog instead of registering a + // synonym" was prose only, and four synonyms accumulated under it without + // anyone deciding to allow them. This is its mechanical form (option C, + // adjudicated 2026-08-12): a synonym registers only with a recorded + // STANDARD_SYNONYM_WAIVERS entry naming the member it shadows. + expect(standardSynonymViolations(ERROR_CODE_LEDGER, STANDARD_SYNONYM_WAIVERS)).toEqual([]); + }); +}); + +describe('standard-synonym detection (#8211)', () => { + it('flags the grandfathered synonyms, each against the member it shadows', () => { + expect(standardSynonymOf('CONFLICT')).toBe('RESOURCE_CONFLICT'); + expect(standardSynonymOf('NOT_FOUND')).toBe('RESOURCE_NOT_FOUND'); + expect(standardSynonymOf('FORBIDDEN')).toBe('PERMISSION_DENIED'); + expect(standardSynonymOf('INTERNAL')).toBe('INTERNAL_ERROR'); + // Surfaced by the detector beyond the four #8211 named — same class + // (401 reason phrase), grandfathered by the same rationale. + expect(standardSynonymOf('UNAUTHORIZED')).toBe('UNAUTHENTICATED'); + }); + + it('does not flag domain-prefixed or catalog-uncovered codes', () => { + // A domain prefix carries a token no standard member has — exactly the + // shape the registration instructions endorse. + expect(standardSynonymOf('FORM_NOT_FOUND')).toBeUndefined(); + expect(standardSynonymOf('ATTACHMENT_DOWNLOAD_DENIED')).toBeUndefined(); + // 413 is deliberately judged against the explicit HttpStatusErrorCodeMap, + // never the bucket fallback: no standard member covers its condition, so + // its reason phrase is a legitimate registration, not a synonym. + expect(standardSynonymOf('PAYLOAD_TOO_LARGE')).toBeUndefined(); + // Near-misses stay admitted: FAILED is not a token of VALIDATION_ERROR. + expect(standardSynonymOf('VALIDATION_FAILED')).toBeUndefined(); + expect(standardSynonymOf('UNSUPPORTED')).toBeUndefined(); + }); + + it('REJECTS a newly-introduced synonym of a standard member — both prongs', () => { + // The hard constraint this card adopted from review: a detector that only + // passes on today's tree is the exact failure mode the prose rule already + // had. Prove the SAME function that gates the real ledger goes red when a + // new synonym lands, once per detection prong. + // Prong 1 — reason-phrase alias (429 → RATE_LIMIT_EXCEEDED): + const withReasonPhrase = { + ...ERROR_CODE_LEDGER, + '@objectstack/rest': [...ERROR_CODE_LEDGER['@objectstack/rest'], 'TOO_MANY_REQUESTS'], + }; + expect(standardSynonymViolations(withReasonPhrase, STANDARD_SYNONYM_WAIVERS)).toEqual([ + { package: '@objectstack/rest', code: 'TOO_MANY_REQUESTS', shadows: 'RATE_LIMIT_EXCEEDED' }, + ]); + // Prong 2 — token subset (RATE_LIMIT ⊆ RATE_LIMIT_EXCEEDED): + const withTokenSubset = { + ...ERROR_CODE_LEDGER, + '@objectstack/core': [...ERROR_CODE_LEDGER['@objectstack/core'], 'RATE_LIMIT'], + }; + expect(standardSynonymViolations(withTokenSubset, STANDARD_SYNONYM_WAIVERS)).toEqual([ + { package: '@objectstack/core', code: 'RATE_LIMIT', shadows: 'RATE_LIMIT_EXCEEDED' }, + ]); + }); + + it('a waiver admits exactly the (code, shadows) pair it records', () => { + const withNew = { + ...ERROR_CODE_LEDGER, + '@objectstack/rest': [...ERROR_CODE_LEDGER['@objectstack/rest'], 'TOO_MANY_REQUESTS'], + }; + const rightWaiver: StandardSynonymWaiver = { + code: 'TOO_MANY_REQUESTS', + shadows: 'RATE_LIMIT_EXCEEDED', + reason: 'test fixture: recorded reason', + }; + expect(standardSynonymViolations(withNew, [...STANDARD_SYNONYM_WAIVERS, rightWaiver])) + .toEqual([]); + // A waiver naming the WRONG member does not admit the code — the recorded + // reason must be about the member actually shadowed. + const wrongWaiver: StandardSynonymWaiver = { + code: 'TOO_MANY_REQUESTS', + shadows: 'QUOTA_EXCEEDED', + reason: 'test fixture: wrong member on purpose', + }; + expect(standardSynonymViolations(withNew, [...STANDARD_SYNONYM_WAIVERS, wrongWaiver])) + .toHaveLength(1); + }); + + it('every waiver is well-formed, still registered, and names the member the detector derives', () => { + const seen = new Set(); + for (const waiver of STANDARD_SYNONYM_WAIVERS) { + const parsed = StandardSynonymWaiverSchema.safeParse(waiver); + expect(parsed.success, `${waiver.code} waiver parses`).toBe(true); + expect(seen.has(waiver.code), `duplicate waiver for ${waiver.code}`).toBe(false); + seen.add(waiver.code); + // Stale-waiver guard: a waiver for a code no longer registered, or one + // the detector no longer flags as a synonym of exactly the member it + // names, is dead weight — it must come out with the condition it waived. + expect(REGISTERED_ERROR_CODES, `${waiver.code} is still registered`).toContain(waiver.code); + expect(standardSynonymOf(waiver.code), `${waiver.code} shadows`).toBe(waiver.shadows); + } + }); + + it('dropping a waiver reddens the gate for every package registering that code', () => { + // Reverse direction, asserted rather than hand-run: the grandfather + // entries are load-bearing, not decorative. + const withoutForbidden = STANDARD_SYNONYM_WAIVERS.filter((w) => w.code !== 'FORBIDDEN'); + const violations = standardSynonymViolations(ERROR_CODE_LEDGER, withoutForbidden); + expect(violations.map((v) => v.package).sort()).toEqual([ + '@objectstack/plugin-approvals', + '@objectstack/plugin-sharing', + '@objectstack/rest', + ]); + expect(new Set(violations.map((v) => v.code))).toEqual(new Set(['FORBIDDEN'])); + expect(new Set(violations.map((v) => v.shadows))).toEqual(new Set(['PERMISSION_DENIED'])); + }); }); describe('ErrorCode (standard ∪ registered)', () => { diff --git a/packages/spec/src/api/error-code-ledger.zod.ts b/packages/spec/src/api/error-code-ledger.zod.ts index 849551c18e..6baca79e1f 100644 --- a/packages/spec/src/api/error-code-ledger.zod.ts +++ b/packages/spec/src/api/error-code-ledger.zod.ts @@ -64,6 +64,28 @@ * condition is generic (not found / permission / validation / rate limit), * use the standard catalog instead of registering a synonym. * + * Since #8211 (adjudicated 2026-08-12, option C) that last sentence is + * MECHANICAL, not prose: the admission gate (`error-code-ledger.test.ts`) + * refuses a new code that {@link standardSynonymOf} maps to a standard-catalog + * member, unless the code carries a {@link STANDARD_SYNONYM_WAIVERS} entry + * recording why it stays and which member it shadows. Four synonyms had + * accumulated by the time the rule got teeth precisely because nothing was + * checking; they (plus a fifth the detector surfaced on landing) are + * grandfathered via waivers below — their wire values are unchanged, and + * consolidating any of them onto its standard member is a deliberate wire + * change DEFERRED by the #8211 adjudication (option B) until a specific code + * has a measured victim. + * + * One rule, two doors (#8087): registration here is the ADMISSION door — what + * the vocabulary may contain. The DISPATCHER door is ruled (#8087, option + * B-as-a-gate, maintainer 2026-08-12) to parse every body it emits against the + * closed vocabulary; until that gate lands, `resolveThrownHttpError` + * (`@objectstack/types`, PR #8088) carries `code` (narrowed) / `declaredCode` + * (verbatim) across the gap. Both doors state the same rule: a code either IS + * the standard member for its condition, or it is registered here — and if it + * merely re-spells a standard member, that registration is a recorded waiver, + * never drift. + * * A code emitted by several packages is listed once per emitting package — * the union dedupes; the per-package rows are provenance, not identity. * @@ -94,7 +116,7 @@ */ import { z } from 'zod'; -import { StandardErrorCode } from './errors.zod'; +import { StandardErrorCode, HttpStatusErrorCodeMap } from './errors.zod'; export const ERROR_CODE_LEDGER = { '@objectstack/rest': [ @@ -438,3 +460,201 @@ export const ErrorCode = z.enum( ) as z.ZodType; export type ErrorCode = StandardErrorCode | RegisteredErrorCode; + +// ========================================== +// Standard-synonym admission rule (#8211) +// ========================================== + +/** + * RFC 9110 / RFC 6585 HTTP reason phrases spelled as SCREAMING_SNAKE — the + * spellings a producer reaches for when naming a condition after its status + * line. Where the phrase was renamed across RFC editions both spellings are + * listed (413, 422). Deliberately the FULL table, not just the statuses the + * catalog names: whether a phrase is a synonym is decided against + * {@link HttpStatusErrorCodeMap} at detection time, so extending that map + * automatically extends this gate — no second list to keep in sync. + */ +const HTTP_REASON_PHRASE_STATUS: Record = { + BAD_REQUEST: 400, + UNAUTHORIZED: 401, + PAYMENT_REQUIRED: 402, + FORBIDDEN: 403, + NOT_FOUND: 404, + METHOD_NOT_ALLOWED: 405, + NOT_ACCEPTABLE: 406, + PROXY_AUTHENTICATION_REQUIRED: 407, + REQUEST_TIMEOUT: 408, + CONFLICT: 409, + GONE: 410, + LENGTH_REQUIRED: 411, + PRECONDITION_FAILED: 412, + PAYLOAD_TOO_LARGE: 413, + CONTENT_TOO_LARGE: 413, + URI_TOO_LONG: 414, + UNSUPPORTED_MEDIA_TYPE: 415, + RANGE_NOT_SATISFIABLE: 416, + EXPECTATION_FAILED: 417, + MISDIRECTED_REQUEST: 421, + UNPROCESSABLE_ENTITY: 422, + UNPROCESSABLE_CONTENT: 422, + LOCKED: 423, + FAILED_DEPENDENCY: 424, + TOO_EARLY: 425, + UPGRADE_REQUIRED: 426, + PRECONDITION_REQUIRED: 428, + TOO_MANY_REQUESTS: 429, + REQUEST_HEADER_FIELDS_TOO_LARGE: 431, + UNAVAILABLE_FOR_LEGAL_REASONS: 451, + INTERNAL_SERVER_ERROR: 500, + NOT_IMPLEMENTED: 501, + BAD_GATEWAY: 502, + SERVICE_UNAVAILABLE: 503, + GATEWAY_TIMEOUT: 504, + HTTP_VERSION_NOT_SUPPORTED: 505, + VARIANT_ALSO_NEGOTIATES: 506, + INSUFFICIENT_STORAGE: 507, + LOOP_DETECTED: 508, + NOT_EXTENDED: 510, + NETWORK_AUTHENTICATION_REQUIRED: 511, +}; + +/** + * The standard-catalog member a registered code is a SEMANTIC SYNONYM of, or + * `undefined` when it is not one (#8211). + * + * A CLOSED, mechanical criterion — two prongs, no distance metrics, no + * wordlists, so every verdict is reproducible from this file alone: + * + * 1. **Reason-phrase alias.** The code is the SCREAMING_SNAKE spelling of an + * HTTP reason phrase whose status {@link HttpStatusErrorCodeMap} maps to a + * standard member: `FORBIDDEN` → 403 → `PERMISSION_DENIED`. Deliberately + * judged against the explicit map only, never the + * `standardErrorCodeForHttpStatus` bucket fallback — a phrase for a status + * the catalog does not name (`PAYLOAD_TOO_LARGE`, 413) is NOT a synonym, + * because no member covers its condition. + * 2. **Token subset.** Every `_`-token of the code appears in one standard + * member's name (`CONFLICT` ⊆ `RESOURCE_CONFLICT`, `INTERNAL` ⊆ + * `INTERNAL_ERROR`): the code says nothing the member's own name does not + * already say. First match in catalog order wins — the generic member leads + * each status block by construction. A domain-prefixed code + * (`FORM_NOT_FOUND`) carries a token no member has, and is exactly the + * shape the registration instructions endorse. + * + * Under-matching is the accepted cost of a closed criterion: a synonym neither + * prong catches (`CONCURRENT_UPDATE` beside `CONCURRENT_MODIFICATION`) is + * admitted. Extend a prong deliberately when a new class is measured — never + * with fuzz. A detector that only passes on today's tree would be this card's + * own failure mode; the admission gate pins rejection of a newly-introduced + * synonym for both prongs. + */ +export function standardSynonymOf(code: string): StandardErrorCode | undefined { + const status = HTTP_REASON_PHRASE_STATUS[code]; + if (status !== undefined) { + const member = HttpStatusErrorCodeMap[status]; + if (member !== undefined && member !== code) return member; + } + const tokens = code.split('_'); + for (const member of StandardErrorCode.options) { + if (member === code) continue; + const memberTokens = new Set(member.split('_')); + if (tokens.every((token) => memberTokens.has(token))) return member; + } + return undefined; +} + +/** + * A recorded admission waiver: why a registered code that + * {@link standardSynonymOf} flags as a semantic synonym of a standard-catalog + * member stays registered anyway (#8211). A waiver names the member it + * shadows and carries a reviewable reason — admission is a decision on the + * record, never drift. It keeps a WIRE VALUE registered; it does not endorse + * the spelling for new code. + */ +export const StandardSynonymWaiverSchema = z.object({ + code: z.string().regex(/^[A-Z][A-Z0-9_]*$/) + .describe('The registered extension code the waiver keeps admissible'), + shadows: StandardErrorCode + .describe('The standard-catalog member whose condition the code re-spells'), + reason: z.string().min(1) + .describe('Why the synonym stays registered — recorded so admission is a decision, not drift'), +}); + +export type StandardSynonymWaiver = z.input; + +/** + * The grandfathered pre-gate synonyms (#8211, adjudicated 2026-08-12, option + * C). Every entry is on the wire today; consolidating any onto the member it + * shadows would change what clients read (`@objectstack/client` surfaces + * `error.code` verbatim) and is DEFERRED — option B — until a specific code + * has a measured victim. The admission gate holds each waiver live: a waiver + * whose code is no longer registered, or that the detector no longer flags as + * a synonym of exactly the member it names, fails the suite and comes out. + */ +export const STANDARD_SYNONYM_WAIVERS: readonly StandardSynonymWaiver[] = [ + { + code: 'CONFLICT', + shadows: 'RESOURCE_CONFLICT', + reason: 'Pre-gate synonym on the wire (respondSharingError 409 arm; registered by #8111). ' + + 'Wire value kept; consolidation deferred per #8211.', + }, + { + code: 'FORBIDDEN', + shadows: 'PERMISSION_DENIED', + reason: 'Pre-gate synonym on the wire from @objectstack/rest, plugin-sharing and ' + + 'plugin-approvals. Wire value kept; consolidation deferred per #8211.', + }, + { + code: 'INTERNAL', + shadows: 'INTERNAL_ERROR', + reason: 'Pre-gate synonym on the wire from five packages. Wire value kept; ' + + 'consolidation deferred per #8211.', + }, + { + code: 'NOT_FOUND', + shadows: 'RESOURCE_NOT_FOUND', + reason: 'Pre-gate synonym on the wire from @objectstack/rest and plugin-sharing. ' + + 'Wire value kept; consolidation deferred per #8211.', + }, + { + code: 'UNAUTHORIZED', + shadows: 'UNAUTHENTICATED', + reason: 'Pre-gate synonym (401 reason phrase) on the wire from @objectstack/rest — ' + + 'surfaced by the detector when the #8211 gate landed, beyond the four the card named; ' + + 'same class, same grandfather rationale. Wire value kept; consolidation deferred per #8211.', + }, +]; + +/** One unwaived semantic-synonym registration, as reported by {@link standardSynonymViolations}. */ +export interface StandardSynonymViolation { + /** The ledger owner key registering the offending code. */ + package: string; + /** The registered code that re-spells a standard member's condition. */ + code: string; + /** The standard-catalog member the code is a synonym of. */ + shadows: StandardErrorCode; +} + +/** + * Every ledger row whose code {@link standardSynonymOf} flags as a semantic + * synonym of a standard-catalog member without a matching + * {@link STANDARD_SYNONYM_WAIVERS} entry (#8211). Empty on an admissible + * ledger — the admission gate in `error-code-ledger.test.ts` asserts exactly + * that, and pins that this same function goes red when a new synonym lands. + * A waiver admits only the exact `(code, shadows)` pair it records. + */ +export function standardSynonymViolations( + ledger: Record = ERROR_CODE_LEDGER, + waivers: readonly StandardSynonymWaiver[] = STANDARD_SYNONYM_WAIVERS, +): StandardSynonymViolation[] { + const waived = new Map(waivers.map((waiver) => [waiver.code, waiver.shadows])); + const violations: StandardSynonymViolation[] = []; + for (const [pkg, codes] of Object.entries(ledger)) { + for (const code of codes) { + const shadows = standardSynonymOf(code); + if (shadows === undefined) continue; + if (waived.get(code) === shadows) continue; + violations.push({ package: pkg, code, shadows }); + } + } + return violations; +} diff --git a/packages/spec/src/type-alias-convention.pin.test.ts b/packages/spec/src/type-alias-convention.pin.test.ts index a7560dc395..4c92cf0961 100644 --- a/packages/spec/src/type-alias-convention.pin.test.ts +++ b/packages/spec/src/type-alias-convention.pin.test.ts @@ -139,6 +139,7 @@ import type * as M61 from './data/driver/common.zod.js'; import type * as M62 from './data/driver/memory.zod.js'; import type * as M63 from './data/driver/sqlite.zod.js'; import type * as M181 from './data/driver/turso.zod.js'; +import type * as M182 from './api/error-code-ledger.zod.js'; import type * as M65 from './data/feed.zod.js'; import type * as M66 from './data/field.zod.js'; import type * as M67 from './data/filter.zod.js'; @@ -263,7 +264,7 @@ import type * as M167 from './ui/view.zod.js'; import type * as M170 from './ui/component.zod.js'; // --------------------------------------------------------------------------- -// 824 isomorphic aliases: `z.input` === `z.infer`, so no `XParsed` is declared. +// 825 isomorphic aliases: `z.input` === `z.infer`, so no `XParsed` is declared. // // That number is machine-checked, not hand-kept. The runtime companion at the // bottom of this file recomputes the pin count from the source and asserts that @@ -398,6 +399,9 @@ export type Iso80 = Assert, z.infer< typeof M20.ErrorCategory > >>; export type Iso82 = Assert, z.infer< typeof M20.StandardErrorCode > >>; export type Iso83 = Assert, z.infer< typeof M20.RetryStrategy > >>; + +// api/error-code-ledger.zod.ts +export type Iso838 = Assert, z.infer< typeof M182.StandardSynonymWaiverSchema > >>; export type Iso84 = Assert, z.infer< typeof M20.FieldErrorCode > >>; export type Iso85 = Assert, z.infer< typeof M20.FieldErrorSchema > >>; @@ -1621,7 +1625,7 @@ describe('ADR-0122 type-alias convention', () => { // this title and the section header above the pin list — are now asserted // against the recomputed count below, so neither can go stale without a red // test naming it. - it('still declares all 824 isomorphic pins', () => { + it('still declares all 825 isomorphic pins', () => { // The truth of each pin is proved by tsc, not here — an `Assert>` // that stops holding is a compile error with the alias named. What tsc // cannot notice is a pin that was DELETED: removing the assertion removes @@ -1816,9 +1820,19 @@ describe('ADR-0122 type-alias convention', () => { // pins are deleted with them, not re-pointed. The ids `Iso335`/`Iso586` // are retired with their subjects and are NOT free for reuse: the ids are // claims about pins, not positions. + // + // 824 -> 825 is #8211's `StandardSynonymWaiverSchema` — the recorded + // waiver that keeps a semantic synonym of a standard-catalog member + // registered in `ERROR_CODE_LEDGER`. Isomorphism MEASURED, not assumed: + // two `z.string()`s (one regex-, one min-constrained — constraints refine, + // they do not reshape) and the `StandardErrorCode` enum, with no + // `.default()`, `.transform()`, `.catch()`, `.optional()` or `.pipe()` + // anywhere, so the two shapes coincide and ADR-0122 gives it a pin rather + // than an `XParsed`. Its id is `Iso838`, the next free one — the ids are + // claims about pins, not positions. const self = readFileSync(fileURLToPath(import.meta.url), 'utf8'); const pins = self.match(/^export type Iso\d+ = Assert Date: Thu, 13 Aug 2026 14:10:24 +0000 Subject: [PATCH 2/4] chore(spec): regenerate api-surface, export-origins, reference docs and strictness-ledger counts for the synonym-gate exports (#8211) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012MNV7ZSCjNfA38eDCjsXQL --- .../docs/references/api/error-code-ledger.mdx | 95 ++++++++++++++++++- content/docs/references/index.mdx | 10 +- ...07-unknown-key-strictness-ledger.counts.md | 2 +- packages/spec/api-surface/api.json | 6 ++ packages/spec/export-origins/api.json | 6 ++ 5 files changed, 111 insertions(+), 8 deletions(-) diff --git a/content/docs/references/api/error-code-ledger.mdx b/content/docs/references/api/error-code-ledger.mdx index 66b0e8e0a5..be8f0d1820 100644 --- a/content/docs/references/api/error-code-ledger.mdx +++ b/content/docs/references/api/error-code-ledger.mdx @@ -68,6 +68,28 @@ self-evidently global (`ATTACHMENT_*`, `REPORT_*`, `SETTINGS_*`). If the condition is generic (not found / permission / validation / rate limit), use the standard catalog instead of registering a synonym. +Since #8211 (adjudicated 2026-08-12, option C) that last sentence is +MECHANICAL, not prose: the admission gate (`error-code-ledger.test.ts`) +refuses a new code that `standardSynonymOf` maps to a standard-catalog +member, unless the code carries a `STANDARD_SYNONYM_WAIVERS` entry +recording why it stays and which member it shadows. Four synonyms had +accumulated by the time the rule got teeth precisely because nothing was +checking; they (plus a fifth the detector surfaced on landing) are +grandfathered via waivers below — their wire values are unchanged, and +consolidating any of them onto its standard member is a deliberate wire +change DEFERRED by the #8211 adjudication (option B) until a specific code +has a measured victim. + +One rule, two doors (#8087): registration here is the ADMISSION door — what +the vocabulary may contain. The DISPATCHER door is ruled (#8087, option +B-as-a-gate, maintainer 2026-08-12) to parse every body it emits against the +closed vocabulary; until that gate lands, `resolveThrownHttpError` +(`@objectstack/types`, PR #8088) carries `code` (narrowed) / `declaredCode` +(verbatim) across the gap. Both doors state the same rule: a code either IS +the standard member for its condition, or it is registered here — and if it +merely re-spells a standard member, that registration is a recorded waiver, +never drift. + A code emitted by several packages is listed once per emitting package — the union dedupes; the per-package rows are provenance, not identity. @@ -103,8 +125,8 @@ SEPARATE vocabulary and do not belong here — see #3977 (ADR-0112 D6). ## TypeScript Usage ```typescript -import { ErrorCode } from '@objectstack/spec/api'; -import type { ErrorCode } from '@objectstack/spec/api'; +import { ErrorCode, StandardSynonymWaiverSchema } from '@objectstack/spec/api'; +import type { ErrorCode, StandardSynonymWaiver } from '@objectstack/spec/api'; // Validate data const result = ErrorCode.parse(data); @@ -388,3 +410,72 @@ const result = ErrorCode.parse(data); --- +## StandardSynonymWaiver + +### Properties + +| 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 | +| **reason** | `string` | ✅ | Why the synonym stays registered — recorded so admission is a decision, not drift | + +### Allowed Values: `StandardSynonymWaiver.shadows` + +* `VALIDATION_ERROR` +* `INVALID_FIELD` +* `MISSING_REQUIRED_FIELD` +* `INVALID_FORMAT` +* `VALUE_TOO_LONG` +* `VALUE_TOO_SHORT` +* `VALUE_OUT_OF_RANGE` +* `INVALID_REFERENCE` +* `DUPLICATE_VALUE` +* `INVALID_QUERY` +* `INVALID_FILTER` +* `INVALID_SORT` +* `MAX_RECORDS_EXCEEDED` +* `UNAUTHENTICATED` +* `INVALID_CREDENTIALS` +* `EXPIRED_TOKEN` +* `INVALID_TOKEN` +* `SESSION_EXPIRED` +* `MFA_REQUIRED` +* `EMAIL_NOT_VERIFIED` +* `PERMISSION_DENIED` +* `INSUFFICIENT_PRIVILEGES` +* `FIELD_NOT_ACCESSIBLE` +* `RECORD_NOT_ACCESSIBLE` +* `LICENSE_REQUIRED` +* `IP_RESTRICTED` +* `TIME_RESTRICTED` +* `RESOURCE_NOT_FOUND` +* `OBJECT_NOT_FOUND` +* `RECORD_NOT_FOUND` +* `FIELD_NOT_FOUND` +* `ENDPOINT_NOT_FOUND` +* `RESOURCE_CONFLICT` +* `CONCURRENT_MODIFICATION` +* `DELETE_RESTRICTED` +* `DUPLICATE_RECORD` +* `LOCK_CONFLICT` +* `METHOD_NOT_ALLOWED` +* `PRECONDITION_REQUIRED` +* `RATE_LIMIT_EXCEEDED` +* `QUOTA_EXCEEDED` +* `CONCURRENT_LIMIT_EXCEEDED` +* `INTERNAL_ERROR` +* `DATABASE_ERROR` +* `TIMEOUT` +* `SERVICE_UNAVAILABLE` +* `NOT_IMPLEMENTED` +* `EXTERNAL_SERVICE_ERROR` +* `INTEGRATION_ERROR` +* `WEBHOOK_DELIVERY_FAILED` +* `BATCH_PARTIAL_FAILURE` +* `BATCH_COMPLETE_FAILURE` +* `TRANSACTION_FAILED` + + +--- + diff --git a/content/docs/references/index.mdx b/content/docs/references/index.mdx index e554ac9100..e7dcd76277 100644 --- a/content/docs/references/index.mdx +++ b/content/docs/references/index.mdx @@ -1,6 +1,6 @@ --- title: Protocol Reference -description: Every schema published by @objectstack/spec — 1570 schemas across 14 protocol modules +description: Every schema published by @objectstack/spec — 1571 schemas across 14 protocol modules --- {/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} @@ -20,7 +20,7 @@ counts are sums of the rows they head. Regenerate with | Module | Pages | Schemas | Description | | :--- | ---: | ---: | :--- | | [AI Protocol](/docs/references/ai) | 11 | 66 | Agents, tools, skills, RAG and knowledge sources, model registry, conversations. | -| [API Protocol](/docs/references/api) | 28 | 412 | REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. | +| [API Protocol](/docs/references/api) | 28 | 413 | REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. | | [Automation Protocol](/docs/references/automation) | 13 | 68 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. | | [Cloud Protocol](/docs/references/cloud) | 11 | 94 | Environments, packages and versions, marketplace, developer portal, tenancy. | | [Data Protocol](/docs/references/data) | 29 | 163 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. | @@ -33,7 +33,7 @@ counts are sums of the rows they head. Regenerate with | [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. | | [System Protocol](/docs/references/system) | 36 | 287 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. | | [UI Protocol](/docs/references/ui) | 16 | 148 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. | -| **Total** | **198** | **1570** | 14 protocol modules | +| **Total** | **198** | **1571** | 14 protocol modules | --- @@ -61,7 +61,7 @@ Agents, tools, skills, RAG and knowledge sources, model registry, conversations. ## API Protocol -**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **28 pages, 412 schemas** +**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **28 pages, 413 schemas** REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. @@ -77,7 +77,7 @@ REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. | [`dispatcher.zod.ts`](/docs/references/api/dispatcher) | `DispatcherConfig`, `DispatcherErrorCode`, `DispatcherErrorResponse`, `DispatcherRoute` | | [`documentation.zod.ts`](/docs/references/api/documentation) | `ApiChangelogEntry`, `ApiDocumentationConfig`, `ApiTestCollection`, `ApiTestRequest`, `ApiTestingUiConfig`, `ApiTestingUiType`, `CodeGenerationTemplate`, `GeneratedApiDocumentation`, `OpenApiSecurityScheme`, `OpenApiServer`, `OpenApiSpec` | | [`endpoint.zod.ts`](/docs/references/api/endpoint) | `ApiEndpoint`, `ApiMapping` | -| [`error-code-ledger.zod.ts`](/docs/references/api/error-code-ledger) | `ErrorCode` | +| [`error-code-ledger.zod.ts`](/docs/references/api/error-code-ledger) | `ErrorCode`, `StandardSynonymWaiver` | | [`errors.zod.ts`](/docs/references/api/errors) | `EnhancedApiError`, `ErrorCategory`, `ErrorResponse`, `FieldError`, `FieldErrorCode`, `RetryStrategy`, `StandardErrorCode` | | [`events.zod.ts`](/docs/references/api/events) | `BulkDataEvent`, `BulkDataEventType`, `DataEvent`, `DataEventType`, `MetadataEvent`, `MetadataEventType` | | [`export.zod.ts`](/docs/references/api/export) | `CreateExportJobRequest`, `CreateExportJobResponse`, `CreateImportJobRequest`, `CreateImportJobResponse`, `DeduplicationStrategy`, `ExportFormat`, `ExportImportTemplate`, `ExportJobProgress`, `ExportJobStatus`, `ExportJobSummary`, `FieldMappingEntry`, `GetExportJobDownloadRequest`, `GetExportJobDownloadResponse`, `ImportJobProgress`, `ImportJobResults`, `ImportJobStatus`, `ImportJobSummary`, `ImportMapping`, `ImportRequest`, `ImportResponse`, `ImportRowResult`, `ImportValidationConfig`, `ImportValidationMode`, `ImportValidationResult`, `ImportWriteMode`, `ListExportJobsRequest`, `ListExportJobsResponse`, `ListImportJobsRequest`, `ListImportJobsResponse`, `ScheduleExportRequest`, `ScheduleExportResponse`, `ScheduledExport`, `UndoImportJobResponse` | diff --git a/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md b/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md index ba56b0c90d..96306796f2 100644 --- a/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md +++ b/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md @@ -260,7 +260,7 @@ directory rather than per file. | Dir | Sites | |---|---| | `ai/` | 77 | -| `api/` | 398 | +| `api/` | 399 | | `cloud/` | 83 | | `identity/` | 33 | | `integration/` | 10 | diff --git a/packages/spec/api-surface/api.json b/packages/spec/api-surface/api.json index f5004d92cc..1039b291f8 100644 --- a/packages/spec/api-surface/api.json +++ b/packages/spec/api-surface/api.json @@ -834,6 +834,7 @@ "RuntimeAuthoringIssue (type)", "RuntimeAuthoringIssueSchema (const)", "SERVICE_SELF_INFO_KEY (const)", + "STANDARD_SYNONYM_WAIVERS (const)", "SaveMetaItemRequest (type)", "SaveMetaItemRequestSchema (const)", "SaveMetaItemResponse (type)", @@ -873,6 +874,9 @@ "SingleRecordResponseSchema (const)", "StandardApiContracts (const)", "StandardErrorCode (type)", + "StandardSynonymViolation (interface)", + "StandardSynonymWaiver (type)", + "StandardSynonymWaiverSchema (const)", "StorageApiContracts (const)", "SubscribeMessage (type)", "SubscribeMessageSchema (const)", @@ -998,6 +1002,8 @@ "readServiceSelfInfo (function)", "resolveDiscoveryEnvironment (function)", "standardErrorCodeForHttpStatus (function)", + "standardSynonymOf (function)", + "standardSynonymViolations (function)", "validateApiEndpointDeclarations (function)", "zodIssuesToFields (function)" ] diff --git a/packages/spec/export-origins/api.json b/packages/spec/export-origins/api.json index 0245035003..bb2386a5c9 100644 --- a/packages/spec/export-origins/api.json +++ b/packages/spec/export-origins/api.json @@ -834,6 +834,7 @@ "RuntimeAuthoringIssue": "src/api/protocol.zod.ts#RuntimeAuthoringIssue (type)", "RuntimeAuthoringIssueSchema": "src/api/protocol.zod.ts#RuntimeAuthoringIssueSchema (const)", "SERVICE_SELF_INFO_KEY": "src/api/discovery.zod.ts#SERVICE_SELF_INFO_KEY (const)", + "STANDARD_SYNONYM_WAIVERS": "src/api/error-code-ledger.zod.ts#STANDARD_SYNONYM_WAIVERS (const)", "SaveMetaItemRequest": "src/api/protocol.zod.ts#SaveMetaItemRequest (type)", "SaveMetaItemRequestSchema": "src/api/protocol.zod.ts#SaveMetaItemRequestSchema (const)", "SaveMetaItemResponse": "src/api/protocol.zod.ts#SaveMetaItemResponse (type)", @@ -873,6 +874,9 @@ "SingleRecordResponseSchema": "src/api/contract.zod.ts#SingleRecordResponseSchema (const)", "StandardApiContracts": "src/api/contract.zod.ts#StandardApiContracts (const)", "StandardErrorCode": "src/api/errors.zod.ts#StandardErrorCode (type)", + "StandardSynonymViolation": "src/api/error-code-ledger.zod.ts#StandardSynonymViolation (interface)", + "StandardSynonymWaiver": "src/api/error-code-ledger.zod.ts#StandardSynonymWaiver (type)", + "StandardSynonymWaiverSchema": "src/api/error-code-ledger.zod.ts#StandardSynonymWaiverSchema (const)", "StorageApiContracts": "src/api/storage.zod.ts#StorageApiContracts (const)", "SubscribeMessage": "src/api/websocket.zod.ts#SubscribeMessage (type)", "SubscribeMessageSchema": "src/api/websocket.zod.ts#SubscribeMessageSchema (const)", @@ -998,6 +1002,8 @@ "readServiceSelfInfo": "src/api/discovery.zod.ts#readServiceSelfInfo (function)", "resolveDiscoveryEnvironment": "src/api/discovery.zod.ts#resolveDiscoveryEnvironment (function)", "standardErrorCodeForHttpStatus": "src/api/errors.zod.ts#standardErrorCodeForHttpStatus (function)", + "standardSynonymOf": "src/api/error-code-ledger.zod.ts#standardSynonymOf (function)", + "standardSynonymViolations": "src/api/error-code-ledger.zod.ts#standardSynonymViolations (function)", "validateApiEndpointDeclarations": "src/api/endpoint-publish-gate.ts#validateApiEndpointDeclarations (function)", "zodIssuesToFields": "src/api/zod-issues-to-fields.ts#zodIssuesToFields (function)" } From 7369f97a71c30d5303aede1a86412a66a59dd8ec Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 16:27:49 +0000 Subject: [PATCH 3/4] merge origin/main (os-regen artifacts taken from main; regeneration follows) --- .../docs/references/api/error-code-ledger.mdx | 95 +------------------ content/docs/references/index.mdx | 10 +- ...07-unknown-key-strictness-ledger.counts.md | 2 +- packages/spec/api-surface/api.json | 6 -- packages/spec/authorable-surface/api.json | 3 - packages/spec/export-origins/api.json | 6 -- packages/spec/json-schema.manifest/api.json | 1 - 7 files changed, 8 insertions(+), 115 deletions(-) diff --git a/content/docs/references/api/error-code-ledger.mdx b/content/docs/references/api/error-code-ledger.mdx index be8f0d1820..66b0e8e0a5 100644 --- a/content/docs/references/api/error-code-ledger.mdx +++ b/content/docs/references/api/error-code-ledger.mdx @@ -68,28 +68,6 @@ self-evidently global (`ATTACHMENT_*`, `REPORT_*`, `SETTINGS_*`). If the condition is generic (not found / permission / validation / rate limit), use the standard catalog instead of registering a synonym. -Since #8211 (adjudicated 2026-08-12, option C) that last sentence is -MECHANICAL, not prose: the admission gate (`error-code-ledger.test.ts`) -refuses a new code that `standardSynonymOf` maps to a standard-catalog -member, unless the code carries a `STANDARD_SYNONYM_WAIVERS` entry -recording why it stays and which member it shadows. Four synonyms had -accumulated by the time the rule got teeth precisely because nothing was -checking; they (plus a fifth the detector surfaced on landing) are -grandfathered via waivers below — their wire values are unchanged, and -consolidating any of them onto its standard member is a deliberate wire -change DEFERRED by the #8211 adjudication (option B) until a specific code -has a measured victim. - -One rule, two doors (#8087): registration here is the ADMISSION door — what -the vocabulary may contain. The DISPATCHER door is ruled (#8087, option -B-as-a-gate, maintainer 2026-08-12) to parse every body it emits against the -closed vocabulary; until that gate lands, `resolveThrownHttpError` -(`@objectstack/types`, PR #8088) carries `code` (narrowed) / `declaredCode` -(verbatim) across the gap. Both doors state the same rule: a code either IS -the standard member for its condition, or it is registered here — and if it -merely re-spells a standard member, that registration is a recorded waiver, -never drift. - A code emitted by several packages is listed once per emitting package — the union dedupes; the per-package rows are provenance, not identity. @@ -125,8 +103,8 @@ SEPARATE vocabulary and do not belong here — see #3977 (ADR-0112 D6). ## TypeScript Usage ```typescript -import { ErrorCode, StandardSynonymWaiverSchema } from '@objectstack/spec/api'; -import type { ErrorCode, StandardSynonymWaiver } from '@objectstack/spec/api'; +import { ErrorCode } from '@objectstack/spec/api'; +import type { ErrorCode } from '@objectstack/spec/api'; // Validate data const result = ErrorCode.parse(data); @@ -410,72 +388,3 @@ const result = ErrorCode.parse(data); --- -## StandardSynonymWaiver - -### Properties - -| 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 | -| **reason** | `string` | ✅ | Why the synonym stays registered — recorded so admission is a decision, not drift | - -### Allowed Values: `StandardSynonymWaiver.shadows` - -* `VALIDATION_ERROR` -* `INVALID_FIELD` -* `MISSING_REQUIRED_FIELD` -* `INVALID_FORMAT` -* `VALUE_TOO_LONG` -* `VALUE_TOO_SHORT` -* `VALUE_OUT_OF_RANGE` -* `INVALID_REFERENCE` -* `DUPLICATE_VALUE` -* `INVALID_QUERY` -* `INVALID_FILTER` -* `INVALID_SORT` -* `MAX_RECORDS_EXCEEDED` -* `UNAUTHENTICATED` -* `INVALID_CREDENTIALS` -* `EXPIRED_TOKEN` -* `INVALID_TOKEN` -* `SESSION_EXPIRED` -* `MFA_REQUIRED` -* `EMAIL_NOT_VERIFIED` -* `PERMISSION_DENIED` -* `INSUFFICIENT_PRIVILEGES` -* `FIELD_NOT_ACCESSIBLE` -* `RECORD_NOT_ACCESSIBLE` -* `LICENSE_REQUIRED` -* `IP_RESTRICTED` -* `TIME_RESTRICTED` -* `RESOURCE_NOT_FOUND` -* `OBJECT_NOT_FOUND` -* `RECORD_NOT_FOUND` -* `FIELD_NOT_FOUND` -* `ENDPOINT_NOT_FOUND` -* `RESOURCE_CONFLICT` -* `CONCURRENT_MODIFICATION` -* `DELETE_RESTRICTED` -* `DUPLICATE_RECORD` -* `LOCK_CONFLICT` -* `METHOD_NOT_ALLOWED` -* `PRECONDITION_REQUIRED` -* `RATE_LIMIT_EXCEEDED` -* `QUOTA_EXCEEDED` -* `CONCURRENT_LIMIT_EXCEEDED` -* `INTERNAL_ERROR` -* `DATABASE_ERROR` -* `TIMEOUT` -* `SERVICE_UNAVAILABLE` -* `NOT_IMPLEMENTED` -* `EXTERNAL_SERVICE_ERROR` -* `INTEGRATION_ERROR` -* `WEBHOOK_DELIVERY_FAILED` -* `BATCH_PARTIAL_FAILURE` -* `BATCH_COMPLETE_FAILURE` -* `TRANSACTION_FAILED` - - ---- - diff --git a/content/docs/references/index.mdx b/content/docs/references/index.mdx index e7dcd76277..e554ac9100 100644 --- a/content/docs/references/index.mdx +++ b/content/docs/references/index.mdx @@ -1,6 +1,6 @@ --- title: Protocol Reference -description: Every schema published by @objectstack/spec — 1571 schemas across 14 protocol modules +description: Every schema published by @objectstack/spec — 1570 schemas across 14 protocol modules --- {/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} @@ -20,7 +20,7 @@ counts are sums of the rows they head. Regenerate with | Module | Pages | Schemas | Description | | :--- | ---: | ---: | :--- | | [AI Protocol](/docs/references/ai) | 11 | 66 | Agents, tools, skills, RAG and knowledge sources, model registry, conversations. | -| [API Protocol](/docs/references/api) | 28 | 413 | REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. | +| [API Protocol](/docs/references/api) | 28 | 412 | REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. | | [Automation Protocol](/docs/references/automation) | 13 | 68 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. | | [Cloud Protocol](/docs/references/cloud) | 11 | 94 | Environments, packages and versions, marketplace, developer portal, tenancy. | | [Data Protocol](/docs/references/data) | 29 | 163 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. | @@ -33,7 +33,7 @@ counts are sums of the rows they head. Regenerate with | [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. | | [System Protocol](/docs/references/system) | 36 | 287 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. | | [UI Protocol](/docs/references/ui) | 16 | 148 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. | -| **Total** | **198** | **1571** | 14 protocol modules | +| **Total** | **198** | **1570** | 14 protocol modules | --- @@ -61,7 +61,7 @@ Agents, tools, skills, RAG and knowledge sources, model registry, conversations. ## API Protocol -**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **28 pages, 413 schemas** +**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **28 pages, 412 schemas** REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. @@ -77,7 +77,7 @@ REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. | [`dispatcher.zod.ts`](/docs/references/api/dispatcher) | `DispatcherConfig`, `DispatcherErrorCode`, `DispatcherErrorResponse`, `DispatcherRoute` | | [`documentation.zod.ts`](/docs/references/api/documentation) | `ApiChangelogEntry`, `ApiDocumentationConfig`, `ApiTestCollection`, `ApiTestRequest`, `ApiTestingUiConfig`, `ApiTestingUiType`, `CodeGenerationTemplate`, `GeneratedApiDocumentation`, `OpenApiSecurityScheme`, `OpenApiServer`, `OpenApiSpec` | | [`endpoint.zod.ts`](/docs/references/api/endpoint) | `ApiEndpoint`, `ApiMapping` | -| [`error-code-ledger.zod.ts`](/docs/references/api/error-code-ledger) | `ErrorCode`, `StandardSynonymWaiver` | +| [`error-code-ledger.zod.ts`](/docs/references/api/error-code-ledger) | `ErrorCode` | | [`errors.zod.ts`](/docs/references/api/errors) | `EnhancedApiError`, `ErrorCategory`, `ErrorResponse`, `FieldError`, `FieldErrorCode`, `RetryStrategy`, `StandardErrorCode` | | [`events.zod.ts`](/docs/references/api/events) | `BulkDataEvent`, `BulkDataEventType`, `DataEvent`, `DataEventType`, `MetadataEvent`, `MetadataEventType` | | [`export.zod.ts`](/docs/references/api/export) | `CreateExportJobRequest`, `CreateExportJobResponse`, `CreateImportJobRequest`, `CreateImportJobResponse`, `DeduplicationStrategy`, `ExportFormat`, `ExportImportTemplate`, `ExportJobProgress`, `ExportJobStatus`, `ExportJobSummary`, `FieldMappingEntry`, `GetExportJobDownloadRequest`, `GetExportJobDownloadResponse`, `ImportJobProgress`, `ImportJobResults`, `ImportJobStatus`, `ImportJobSummary`, `ImportMapping`, `ImportRequest`, `ImportResponse`, `ImportRowResult`, `ImportValidationConfig`, `ImportValidationMode`, `ImportValidationResult`, `ImportWriteMode`, `ListExportJobsRequest`, `ListExportJobsResponse`, `ListImportJobsRequest`, `ListImportJobsResponse`, `ScheduleExportRequest`, `ScheduleExportResponse`, `ScheduledExport`, `UndoImportJobResponse` | diff --git a/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md b/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md index 96306796f2..ba56b0c90d 100644 --- a/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md +++ b/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md @@ -260,7 +260,7 @@ directory rather than per file. | Dir | Sites | |---|---| | `ai/` | 77 | -| `api/` | 399 | +| `api/` | 398 | | `cloud/` | 83 | | `identity/` | 33 | | `integration/` | 10 | diff --git a/packages/spec/api-surface/api.json b/packages/spec/api-surface/api.json index 1039b291f8..f5004d92cc 100644 --- a/packages/spec/api-surface/api.json +++ b/packages/spec/api-surface/api.json @@ -834,7 +834,6 @@ "RuntimeAuthoringIssue (type)", "RuntimeAuthoringIssueSchema (const)", "SERVICE_SELF_INFO_KEY (const)", - "STANDARD_SYNONYM_WAIVERS (const)", "SaveMetaItemRequest (type)", "SaveMetaItemRequestSchema (const)", "SaveMetaItemResponse (type)", @@ -874,9 +873,6 @@ "SingleRecordResponseSchema (const)", "StandardApiContracts (const)", "StandardErrorCode (type)", - "StandardSynonymViolation (interface)", - "StandardSynonymWaiver (type)", - "StandardSynonymWaiverSchema (const)", "StorageApiContracts (const)", "SubscribeMessage (type)", "SubscribeMessageSchema (const)", @@ -1002,8 +998,6 @@ "readServiceSelfInfo (function)", "resolveDiscoveryEnvironment (function)", "standardErrorCodeForHttpStatus (function)", - "standardSynonymOf (function)", - "standardSynonymViolations (function)", "validateApiEndpointDeclarations (function)", "zodIssuesToFields (function)" ] diff --git a/packages/spec/authorable-surface/api.json b/packages/spec/authorable-surface/api.json index 6d968574cb..fc2adf9985 100644 --- a/packages/spec/authorable-surface/api.json +++ b/packages/spec/authorable-surface/api.json @@ -1538,9 +1538,6 @@ "api/SingleRecordResponse:error", "api/SingleRecordResponse:meta", "api/SingleRecordResponse:success", - "api/StandardSynonymWaiver:code", - "api/StandardSynonymWaiver:reason", - "api/StandardSynonymWaiver:shadows", "api/SubscribeMessage:messageId", "api/SubscribeMessage:subscription", "api/SubscribeMessage:timestamp", diff --git a/packages/spec/export-origins/api.json b/packages/spec/export-origins/api.json index bb2386a5c9..0245035003 100644 --- a/packages/spec/export-origins/api.json +++ b/packages/spec/export-origins/api.json @@ -834,7 +834,6 @@ "RuntimeAuthoringIssue": "src/api/protocol.zod.ts#RuntimeAuthoringIssue (type)", "RuntimeAuthoringIssueSchema": "src/api/protocol.zod.ts#RuntimeAuthoringIssueSchema (const)", "SERVICE_SELF_INFO_KEY": "src/api/discovery.zod.ts#SERVICE_SELF_INFO_KEY (const)", - "STANDARD_SYNONYM_WAIVERS": "src/api/error-code-ledger.zod.ts#STANDARD_SYNONYM_WAIVERS (const)", "SaveMetaItemRequest": "src/api/protocol.zod.ts#SaveMetaItemRequest (type)", "SaveMetaItemRequestSchema": "src/api/protocol.zod.ts#SaveMetaItemRequestSchema (const)", "SaveMetaItemResponse": "src/api/protocol.zod.ts#SaveMetaItemResponse (type)", @@ -874,9 +873,6 @@ "SingleRecordResponseSchema": "src/api/contract.zod.ts#SingleRecordResponseSchema (const)", "StandardApiContracts": "src/api/contract.zod.ts#StandardApiContracts (const)", "StandardErrorCode": "src/api/errors.zod.ts#StandardErrorCode (type)", - "StandardSynonymViolation": "src/api/error-code-ledger.zod.ts#StandardSynonymViolation (interface)", - "StandardSynonymWaiver": "src/api/error-code-ledger.zod.ts#StandardSynonymWaiver (type)", - "StandardSynonymWaiverSchema": "src/api/error-code-ledger.zod.ts#StandardSynonymWaiverSchema (const)", "StorageApiContracts": "src/api/storage.zod.ts#StorageApiContracts (const)", "SubscribeMessage": "src/api/websocket.zod.ts#SubscribeMessage (type)", "SubscribeMessageSchema": "src/api/websocket.zod.ts#SubscribeMessageSchema (const)", @@ -1002,8 +998,6 @@ "readServiceSelfInfo": "src/api/discovery.zod.ts#readServiceSelfInfo (function)", "resolveDiscoveryEnvironment": "src/api/discovery.zod.ts#resolveDiscoveryEnvironment (function)", "standardErrorCodeForHttpStatus": "src/api/errors.zod.ts#standardErrorCodeForHttpStatus (function)", - "standardSynonymOf": "src/api/error-code-ledger.zod.ts#standardSynonymOf (function)", - "standardSynonymViolations": "src/api/error-code-ledger.zod.ts#standardSynonymViolations (function)", "validateApiEndpointDeclarations": "src/api/endpoint-publish-gate.ts#validateApiEndpointDeclarations (function)", "zodIssuesToFields": "src/api/zod-issues-to-fields.ts#zodIssuesToFields (function)" } diff --git a/packages/spec/json-schema.manifest/api.json b/packages/spec/json-schema.manifest/api.json index 26b2dcaccd..c86259f937 100644 --- a/packages/spec/json-schema.manifest/api.json +++ b/packages/spec/json-schema.manifest/api.json @@ -363,7 +363,6 @@ "api/SimplePresenceState", "api/SingleRecordResponse", "api/StandardErrorCode", - "api/StandardSynonymWaiver", "api/SubscribeMessage", "api/Subscription", "api/SubscriptionEvent", From 11bd30cef4489115de805b85e89070e83c4dfbfd Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 16:46:41 +0000 Subject: [PATCH 4/4] chore(spec): regenerate os-regen artifacts on the merged tree (#8211 relay after #8457) Discharges the os-regen deferral from the origin/main merge: api-surface, export-origins, reference docs, strictness-ledger counts re-derived on the merged base so the union carries both #8457's entries and the #8211 synonym-gate exports. gen:openapi re-run after the chain per the script's warning. Sibling assertions green (datasource-config-placeholder-refused registered; placeholderFree/containsUnresolvedPlaceholder body present). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_012MNV7ZSCjNfA38eDCjsXQL --- .../docs/references/api/error-code-ledger.mdx | 95 ++++++++++++++++++- content/docs/references/index.mdx | 10 +- ...07-unknown-key-strictness-ledger.counts.md | 2 +- packages/spec/api-surface/api.json | 6 ++ packages/spec/authorable-surface/api.json | 3 + packages/spec/export-origins/api.json | 6 ++ packages/spec/json-schema.manifest/api.json | 1 + 7 files changed, 115 insertions(+), 8 deletions(-) diff --git a/content/docs/references/api/error-code-ledger.mdx b/content/docs/references/api/error-code-ledger.mdx index 66b0e8e0a5..be8f0d1820 100644 --- a/content/docs/references/api/error-code-ledger.mdx +++ b/content/docs/references/api/error-code-ledger.mdx @@ -68,6 +68,28 @@ self-evidently global (`ATTACHMENT_*`, `REPORT_*`, `SETTINGS_*`). If the condition is generic (not found / permission / validation / rate limit), use the standard catalog instead of registering a synonym. +Since #8211 (adjudicated 2026-08-12, option C) that last sentence is +MECHANICAL, not prose: the admission gate (`error-code-ledger.test.ts`) +refuses a new code that `standardSynonymOf` maps to a standard-catalog +member, unless the code carries a `STANDARD_SYNONYM_WAIVERS` entry +recording why it stays and which member it shadows. Four synonyms had +accumulated by the time the rule got teeth precisely because nothing was +checking; they (plus a fifth the detector surfaced on landing) are +grandfathered via waivers below — their wire values are unchanged, and +consolidating any of them onto its standard member is a deliberate wire +change DEFERRED by the #8211 adjudication (option B) until a specific code +has a measured victim. + +One rule, two doors (#8087): registration here is the ADMISSION door — what +the vocabulary may contain. The DISPATCHER door is ruled (#8087, option +B-as-a-gate, maintainer 2026-08-12) to parse every body it emits against the +closed vocabulary; until that gate lands, `resolveThrownHttpError` +(`@objectstack/types`, PR #8088) carries `code` (narrowed) / `declaredCode` +(verbatim) across the gap. Both doors state the same rule: a code either IS +the standard member for its condition, or it is registered here — and if it +merely re-spells a standard member, that registration is a recorded waiver, +never drift. + A code emitted by several packages is listed once per emitting package — the union dedupes; the per-package rows are provenance, not identity. @@ -103,8 +125,8 @@ SEPARATE vocabulary and do not belong here — see #3977 (ADR-0112 D6). ## TypeScript Usage ```typescript -import { ErrorCode } from '@objectstack/spec/api'; -import type { ErrorCode } from '@objectstack/spec/api'; +import { ErrorCode, StandardSynonymWaiverSchema } from '@objectstack/spec/api'; +import type { ErrorCode, StandardSynonymWaiver } from '@objectstack/spec/api'; // Validate data const result = ErrorCode.parse(data); @@ -388,3 +410,72 @@ const result = ErrorCode.parse(data); --- +## StandardSynonymWaiver + +### Properties + +| 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 | +| **reason** | `string` | ✅ | Why the synonym stays registered — recorded so admission is a decision, not drift | + +### Allowed Values: `StandardSynonymWaiver.shadows` + +* `VALIDATION_ERROR` +* `INVALID_FIELD` +* `MISSING_REQUIRED_FIELD` +* `INVALID_FORMAT` +* `VALUE_TOO_LONG` +* `VALUE_TOO_SHORT` +* `VALUE_OUT_OF_RANGE` +* `INVALID_REFERENCE` +* `DUPLICATE_VALUE` +* `INVALID_QUERY` +* `INVALID_FILTER` +* `INVALID_SORT` +* `MAX_RECORDS_EXCEEDED` +* `UNAUTHENTICATED` +* `INVALID_CREDENTIALS` +* `EXPIRED_TOKEN` +* `INVALID_TOKEN` +* `SESSION_EXPIRED` +* `MFA_REQUIRED` +* `EMAIL_NOT_VERIFIED` +* `PERMISSION_DENIED` +* `INSUFFICIENT_PRIVILEGES` +* `FIELD_NOT_ACCESSIBLE` +* `RECORD_NOT_ACCESSIBLE` +* `LICENSE_REQUIRED` +* `IP_RESTRICTED` +* `TIME_RESTRICTED` +* `RESOURCE_NOT_FOUND` +* `OBJECT_NOT_FOUND` +* `RECORD_NOT_FOUND` +* `FIELD_NOT_FOUND` +* `ENDPOINT_NOT_FOUND` +* `RESOURCE_CONFLICT` +* `CONCURRENT_MODIFICATION` +* `DELETE_RESTRICTED` +* `DUPLICATE_RECORD` +* `LOCK_CONFLICT` +* `METHOD_NOT_ALLOWED` +* `PRECONDITION_REQUIRED` +* `RATE_LIMIT_EXCEEDED` +* `QUOTA_EXCEEDED` +* `CONCURRENT_LIMIT_EXCEEDED` +* `INTERNAL_ERROR` +* `DATABASE_ERROR` +* `TIMEOUT` +* `SERVICE_UNAVAILABLE` +* `NOT_IMPLEMENTED` +* `EXTERNAL_SERVICE_ERROR` +* `INTEGRATION_ERROR` +* `WEBHOOK_DELIVERY_FAILED` +* `BATCH_PARTIAL_FAILURE` +* `BATCH_COMPLETE_FAILURE` +* `TRANSACTION_FAILED` + + +--- + diff --git a/content/docs/references/index.mdx b/content/docs/references/index.mdx index e554ac9100..e7dcd76277 100644 --- a/content/docs/references/index.mdx +++ b/content/docs/references/index.mdx @@ -1,6 +1,6 @@ --- title: Protocol Reference -description: Every schema published by @objectstack/spec — 1570 schemas across 14 protocol modules +description: Every schema published by @objectstack/spec — 1571 schemas across 14 protocol modules --- {/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */} @@ -20,7 +20,7 @@ counts are sums of the rows they head. Regenerate with | Module | Pages | Schemas | Description | | :--- | ---: | ---: | :--- | | [AI Protocol](/docs/references/ai) | 11 | 66 | Agents, tools, skills, RAG and knowledge sources, model registry, conversations. | -| [API Protocol](/docs/references/api) | 28 | 412 | REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. | +| [API Protocol](/docs/references/api) | 28 | 413 | REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. | | [Automation Protocol](/docs/references/automation) | 13 | 68 | Flows and their nodes, approvals, ETL pipelines, webhooks, state machines, execution records. | | [Cloud Protocol](/docs/references/cloud) | 11 | 94 | Environments, packages and versions, marketplace, developer portal, tenancy. | | [Data Protocol](/docs/references/data) | 29 | 163 | Objects, fields, queries, filters, datasources and drivers — the ObjectQL layer. | @@ -33,7 +33,7 @@ counts are sums of the rows they head. Regenerate with | [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. | | [System Protocol](/docs/references/system) | 36 | 287 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. | | [UI Protocol](/docs/references/ui) | 16 | 148 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. | -| **Total** | **198** | **1570** | 14 protocol modules | +| **Total** | **198** | **1571** | 14 protocol modules | --- @@ -61,7 +61,7 @@ Agents, tools, skills, RAG and knowledge sources, model registry, conversations. ## API Protocol -**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **28 pages, 412 schemas** +**Source:** `packages/spec/src/api/` · **Import:** `@objectstack/spec/api` · **28 pages, 413 schemas** REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. @@ -77,7 +77,7 @@ REST/GraphQL contracts, endpoints, routing, realtime, batch, discovery. | [`dispatcher.zod.ts`](/docs/references/api/dispatcher) | `DispatcherConfig`, `DispatcherErrorCode`, `DispatcherErrorResponse`, `DispatcherRoute` | | [`documentation.zod.ts`](/docs/references/api/documentation) | `ApiChangelogEntry`, `ApiDocumentationConfig`, `ApiTestCollection`, `ApiTestRequest`, `ApiTestingUiConfig`, `ApiTestingUiType`, `CodeGenerationTemplate`, `GeneratedApiDocumentation`, `OpenApiSecurityScheme`, `OpenApiServer`, `OpenApiSpec` | | [`endpoint.zod.ts`](/docs/references/api/endpoint) | `ApiEndpoint`, `ApiMapping` | -| [`error-code-ledger.zod.ts`](/docs/references/api/error-code-ledger) | `ErrorCode` | +| [`error-code-ledger.zod.ts`](/docs/references/api/error-code-ledger) | `ErrorCode`, `StandardSynonymWaiver` | | [`errors.zod.ts`](/docs/references/api/errors) | `EnhancedApiError`, `ErrorCategory`, `ErrorResponse`, `FieldError`, `FieldErrorCode`, `RetryStrategy`, `StandardErrorCode` | | [`events.zod.ts`](/docs/references/api/events) | `BulkDataEvent`, `BulkDataEventType`, `DataEvent`, `DataEventType`, `MetadataEvent`, `MetadataEventType` | | [`export.zod.ts`](/docs/references/api/export) | `CreateExportJobRequest`, `CreateExportJobResponse`, `CreateImportJobRequest`, `CreateImportJobResponse`, `DeduplicationStrategy`, `ExportFormat`, `ExportImportTemplate`, `ExportJobProgress`, `ExportJobStatus`, `ExportJobSummary`, `FieldMappingEntry`, `GetExportJobDownloadRequest`, `GetExportJobDownloadResponse`, `ImportJobProgress`, `ImportJobResults`, `ImportJobStatus`, `ImportJobSummary`, `ImportMapping`, `ImportRequest`, `ImportResponse`, `ImportRowResult`, `ImportValidationConfig`, `ImportValidationMode`, `ImportValidationResult`, `ImportWriteMode`, `ListExportJobsRequest`, `ListExportJobsResponse`, `ListImportJobsRequest`, `ListImportJobsResponse`, `ScheduleExportRequest`, `ScheduleExportResponse`, `ScheduledExport`, `UndoImportJobResponse` | diff --git a/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md b/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md index ba56b0c90d..96306796f2 100644 --- a/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md +++ b/docs/audits/2026-07-unknown-key-strictness-ledger.counts.md @@ -260,7 +260,7 @@ directory rather than per file. | Dir | Sites | |---|---| | `ai/` | 77 | -| `api/` | 398 | +| `api/` | 399 | | `cloud/` | 83 | | `identity/` | 33 | | `integration/` | 10 | diff --git a/packages/spec/api-surface/api.json b/packages/spec/api-surface/api.json index f5004d92cc..1039b291f8 100644 --- a/packages/spec/api-surface/api.json +++ b/packages/spec/api-surface/api.json @@ -834,6 +834,7 @@ "RuntimeAuthoringIssue (type)", "RuntimeAuthoringIssueSchema (const)", "SERVICE_SELF_INFO_KEY (const)", + "STANDARD_SYNONYM_WAIVERS (const)", "SaveMetaItemRequest (type)", "SaveMetaItemRequestSchema (const)", "SaveMetaItemResponse (type)", @@ -873,6 +874,9 @@ "SingleRecordResponseSchema (const)", "StandardApiContracts (const)", "StandardErrorCode (type)", + "StandardSynonymViolation (interface)", + "StandardSynonymWaiver (type)", + "StandardSynonymWaiverSchema (const)", "StorageApiContracts (const)", "SubscribeMessage (type)", "SubscribeMessageSchema (const)", @@ -998,6 +1002,8 @@ "readServiceSelfInfo (function)", "resolveDiscoveryEnvironment (function)", "standardErrorCodeForHttpStatus (function)", + "standardSynonymOf (function)", + "standardSynonymViolations (function)", "validateApiEndpointDeclarations (function)", "zodIssuesToFields (function)" ] diff --git a/packages/spec/authorable-surface/api.json b/packages/spec/authorable-surface/api.json index fc2adf9985..6d968574cb 100644 --- a/packages/spec/authorable-surface/api.json +++ b/packages/spec/authorable-surface/api.json @@ -1538,6 +1538,9 @@ "api/SingleRecordResponse:error", "api/SingleRecordResponse:meta", "api/SingleRecordResponse:success", + "api/StandardSynonymWaiver:code", + "api/StandardSynonymWaiver:reason", + "api/StandardSynonymWaiver:shadows", "api/SubscribeMessage:messageId", "api/SubscribeMessage:subscription", "api/SubscribeMessage:timestamp", diff --git a/packages/spec/export-origins/api.json b/packages/spec/export-origins/api.json index 0245035003..bb2386a5c9 100644 --- a/packages/spec/export-origins/api.json +++ b/packages/spec/export-origins/api.json @@ -834,6 +834,7 @@ "RuntimeAuthoringIssue": "src/api/protocol.zod.ts#RuntimeAuthoringIssue (type)", "RuntimeAuthoringIssueSchema": "src/api/protocol.zod.ts#RuntimeAuthoringIssueSchema (const)", "SERVICE_SELF_INFO_KEY": "src/api/discovery.zod.ts#SERVICE_SELF_INFO_KEY (const)", + "STANDARD_SYNONYM_WAIVERS": "src/api/error-code-ledger.zod.ts#STANDARD_SYNONYM_WAIVERS (const)", "SaveMetaItemRequest": "src/api/protocol.zod.ts#SaveMetaItemRequest (type)", "SaveMetaItemRequestSchema": "src/api/protocol.zod.ts#SaveMetaItemRequestSchema (const)", "SaveMetaItemResponse": "src/api/protocol.zod.ts#SaveMetaItemResponse (type)", @@ -873,6 +874,9 @@ "SingleRecordResponseSchema": "src/api/contract.zod.ts#SingleRecordResponseSchema (const)", "StandardApiContracts": "src/api/contract.zod.ts#StandardApiContracts (const)", "StandardErrorCode": "src/api/errors.zod.ts#StandardErrorCode (type)", + "StandardSynonymViolation": "src/api/error-code-ledger.zod.ts#StandardSynonymViolation (interface)", + "StandardSynonymWaiver": "src/api/error-code-ledger.zod.ts#StandardSynonymWaiver (type)", + "StandardSynonymWaiverSchema": "src/api/error-code-ledger.zod.ts#StandardSynonymWaiverSchema (const)", "StorageApiContracts": "src/api/storage.zod.ts#StorageApiContracts (const)", "SubscribeMessage": "src/api/websocket.zod.ts#SubscribeMessage (type)", "SubscribeMessageSchema": "src/api/websocket.zod.ts#SubscribeMessageSchema (const)", @@ -998,6 +1002,8 @@ "readServiceSelfInfo": "src/api/discovery.zod.ts#readServiceSelfInfo (function)", "resolveDiscoveryEnvironment": "src/api/discovery.zod.ts#resolveDiscoveryEnvironment (function)", "standardErrorCodeForHttpStatus": "src/api/errors.zod.ts#standardErrorCodeForHttpStatus (function)", + "standardSynonymOf": "src/api/error-code-ledger.zod.ts#standardSynonymOf (function)", + "standardSynonymViolations": "src/api/error-code-ledger.zod.ts#standardSynonymViolations (function)", "validateApiEndpointDeclarations": "src/api/endpoint-publish-gate.ts#validateApiEndpointDeclarations (function)", "zodIssuesToFields": "src/api/zod-issues-to-fields.ts#zodIssuesToFields (function)" } diff --git a/packages/spec/json-schema.manifest/api.json b/packages/spec/json-schema.manifest/api.json index c86259f937..26b2dcaccd 100644 --- a/packages/spec/json-schema.manifest/api.json +++ b/packages/spec/json-schema.manifest/api.json @@ -363,6 +363,7 @@ "api/SimplePresenceState", "api/SingleRecordResponse", "api/StandardErrorCode", + "api/StandardSynonymWaiver", "api/SubscribeMessage", "api/Subscription", "api/SubscriptionEvent",