Skip to content

Commit bc56d78

Browse files
committed
fix(spec): name all THREE withhold arms the refusal relay must move — the third is @objectstack/runtime's dispatcher exit
The published TSDoc, both `.describe()` caveats and the changeset treated the two `@objectstack/rest` arms as the closed set. `errorResponseBase` (`packages/runtime/src/dispatcher-plugin.ts`, gated on `serverFaultProvenance(thrown) === 'declared'`) withholds every declared 5xx too, never consults `declaredServerFaultAnswer`, is mounted by `objectstack serve`, and emits the very envelope this field lands on. The prose now names it, hands the runtime exit to #16146's sub-issue #17153, and: - drops the "13" census count, narrows the copy-off-a-caught-error claim to the `Object.assign` shape, and names the two overlay-delete rewraps in `metadata-protocol/src/protocol.ts` that copy `status` and carry `code`/`userMessage` through `carry*` helpers (⛔ no `carryRefusal`); - notes the analytics dataset door calls arm 1 bare (no `withDeclaredUserMessage`), so the REST arms are not byte-identical when `userMessage` is declared; - replaces the `/references` routing attribution: the throw spells `status`, so it takes `resolveErrorResponse`'s status passthrough into that function's own 5xx arm; a `statusCode` spelling falls to `mapDataError` and arm 1; - qualifies the table's second row as the rule at the three arms, not a platform-wide invariant (the heuristic-only doors read no declaration); - pins the shipped JSON Schema's `const: true` on both envelopes through the generator's own `toJSONSchema` options; - adds the `refusal?: true` line to the hand-written `EnhancedApiError` excerpt in `content/docs/api/error-catalog.mdx`; - regenerates the 66 reference rows for the id-less caveat that no longer says "REST". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
1 parent ef9c0e9 commit bc56d78

17 files changed

Lines changed: 153 additions & 89 deletions

.changeset/adr-0112-envelope-refusal-declaration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ The three cases are now documented side by side on the envelope's TSDoc:
1414

1515
Purely additive: a producer that says nothing here gets exactly the previous behaviour. `true` is the only value — `refusal: false` fails parse instead of becoming a third state consumers would have to interpret. `userMessage` is orthogonal (end-user text; it never replaces `message`) and may ride the same envelope; the TSDoc reconciles this flag with the recorded reason `userMessage` is a text-carrying field rather than "a boolean beside `message`".
1616

17-
This is the spec half. The relay half — the two withhold arms in `@objectstack/rest` (`declaredServerFaultAnswer`, and `resolveErrorResponse`'s own 5xx passthrough arm, which the `/references` door reaches) reading the declaration, and retiring the route-local patch from PR #16143 on `/meta/:type/:name/references` — is #16146; until it lands, a declared refusal is still withheld at the wire.
17+
This is the spec half. The relay half — the three withhold arms reading the declaration (two in `@objectstack/rest`: `declaredServerFaultAnswer`, and `resolveErrorResponse`'s own 5xx passthrough arm, which the `/references` door reaches; one at `@objectstack/runtime`'s dispatcher exit, `errorResponseBase`, which `objectstack serve` mounts and which never consults the first), plus retiring the route-local patch from PR #16143 on `/meta/:type/:name/references` — is #16146 for the REST pair and its sub-issue #17153 for the runtime exit; until they land, a declared refusal is still withheld at the wire.

content/docs/api/error-catalog.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ Every error response follows the `EnhancedApiError` schema:
664664
interface EnhancedApiError {
665665
code: StandardErrorCode; // Machine-readable error code
666666
message: string; // Human-readable description
667+
refusal?: true; // Producer-declared refusal: keeps message on a 5xx
667668
category?: ErrorCategory; // Error category
668669
httpStatus?: number; // HTTP status code
669670
retryable: boolean; // Whether retry may succeed

content/docs/references/api/analytics.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const result = AnalyticsEndpoint.parse(data);
5656
| **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) |
5757
| **message** | `string` || Readable error message |
5858
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces `message`. |
59-
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the REST withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
59+
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
6060
| **category** | `string` | optional | Error category (e.g. validation, authorization) |
6161
| **httpStatus** | `integer` | optional | HTTP status of the response carrying this error |
6262
| **details** | `any` | optional | Additional error context (e.g. field validation errors) |
@@ -122,7 +122,7 @@ const result = AnalyticsEndpoint.parse(data);
122122
| **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) |
123123
| **message** | `string` || Readable error message |
124124
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces `message`. |
125-
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the REST withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
125+
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
126126
| **category** | `string` | optional | Error category (e.g. validation, authorization) |
127127
| **httpStatus** | `integer` | optional | HTTP status of the response carrying this error |
128128
| **details** | `any` | optional | Additional error context (e.g. field validation errors) |
@@ -159,7 +159,7 @@ const result = AnalyticsEndpoint.parse(data);
159159
| **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) |
160160
| **message** | `string` || Readable error message |
161161
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces `message`. |
162-
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the REST withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
162+
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
163163
| **category** | `string` | optional | Error category (e.g. validation, authorization) |
164164
| **httpStatus** | `integer` | optional | HTTP status of the response carrying this error |
165165
| **details** | `any` | optional | Additional error context (e.g. field validation errors) |

content/docs/references/api/auth.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ const result = AuthProvider.parse(data);
129129
| **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) |
130130
| **message** | `string` || Readable error message |
131131
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces `message`. |
132-
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the REST withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
132+
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
133133
| **category** | `string` | optional | Error category (e.g. validation, authorization) |
134134
| **httpStatus** | `integer` | optional | HTTP status of the response carrying this error |
135135
| **details** | `any` | optional | Additional error context (e.g. field validation errors) |
@@ -187,7 +187,7 @@ const result = AuthProvider.parse(data);
187187
| **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) |
188188
| **message** | `string` || Readable error message |
189189
| **userMessage** | `string` | optional | Producer-marked user-facing refusal text, verbatim. Present exactly when the producer opted in at throw time; consumers render it to end users and keep their generic substitution for anything unmarked. Status-agnostic; never replaces `message`. |
190-
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the REST withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
190+
| **refusal** | `true` | optional | Producer-declared: the 5xx this envelope carries is a deliberate refusal whose `message` is authored for the caller, so a boundary that reads the declaration keeps it verbatim (until the withhold arms read it, a declared refusal is still withheld). Absent (the default) on a declared fault, whose `message` is withheld from the body and logged for the operator; redundant on a 4xx. Presence is the declaration — `true` is the only value. |
191191
| **category** | `string` | optional | Error category (e.g. validation, authorization) |
192192
| **httpStatus** | `integer` | optional | HTTP status of the response carrying this error |
193193
| **details** | `any` | optional | Additional error context (e.g. field validation errors) |

0 commit comments

Comments
 (0)