You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Filed unassigned while implementing #11719. Out of scope there: that card's declared region is declaredCode, and its claim rulings fix the scope at that one field.
Its docblock states the contract in as many words — the producer-side opt-in channel for "this exact text is addressed to the END USER" (#9934; maintainer ruling 2026-08-19 on objectui#5210, option 1), where presence IS the marking and a consumer that sees the field renders it verbatim instead of substituting its generic string.
packages/types/src/response-envelope.ts types the shared nested-envelope writer's extra as a four-member Pick over ApiError:
userMessage is not a member, so no route answering the nested envelope can put it on the wire — it is a compile error to try. Measured directly, not inferred: a probe passing { userMessage: 'x' } is refused with TS2353 … 'userMessage' does not exist in type 'Pick<…>', on the same instrument that compiled a control line passing details clean.
Why it is the same class as #11719, and why it is a separate card
Identical shape — a channel ApiErrorSchema declares, that the ONE shared writer cannot emit — and the same one-directional silence: nothing invalid ships, so no gate and no conformance suite notices, while the producer's deliberate text is gone and a consumer told to read the field finds nothing.
It is separate because the two fields answer different rulings and have different producers. declaredCode is ADR-0112's demote (#9106 / #9232) and is produced by resolveThrownHttpError; userMessage is #9934's user-facing-text opt-in and is produced by declaredUserMessage, with a live sandbox-side reader in packages/runtime/src/sandbox/quickjs-runner.ts. #11719's claim rulings scope that card to declaredCode explicitly, so widening its PR to cover this would have been the scope breach those rulings exist to prevent.
packages/rest/src/error-response.ts:558 reads declaredUserMessage(error) for the flat /data door, so the same asymmetry #11719 records for declaredCode — flat door carries it, nested door cannot — applies here.
Not established here
Blast radius. Which nested-envelope routes can actually catch an error carrying a producer userMessage was not censused. The types: sendError's extra cannot carry declaredCode, so every nested-envelope route silently drops an unregistered producer's own code spelling #11719 census found that six of the seven modules on the shared pair build code/message as literals and never consult the shared resolver at all, so for those the field would be inert until they adopt it; packages/rest/src/package-routes.ts is the one that already holds a resolved ThrownHttpError (and therefore its userMessage) at the sendError call.
Whether the repair is widening the Pick again or something that stops this recurring per field. Three of ApiError's optional fields are now known to be declared-but-unemittable-from-the-nested-writer in sequence, which may argue for a different shape than a Pick extended one card at a time.
Severity not judged.
Region: packages/types/src/response-envelope.ts, sendError's extra parameter.
Filed unassigned while implementing #11719. Out of scope there: that card's declared region is
declaredCode, and its claim rulings fix the scope at that one field.Measured on
origin/main@cdbd920ApiErrorSchema(packages/spec/src/api/contract.zod.ts:87) declaresuserMessagebesidedeclaredCode:Its docblock states the contract in as many words — the producer-side opt-in channel for "this exact text is addressed to the END USER" (#9934; maintainer ruling 2026-08-19 on objectui#5210, option 1), where presence IS the marking and a consumer that sees the field renders it verbatim instead of substituting its generic string.
packages/types/src/response-envelope.tstypes the shared nested-envelope writer'sextraas a four-memberPickoverApiError:userMessageis not a member, so no route answering the nested envelope can put it on the wire — it is a compile error to try. Measured directly, not inferred: a probe passing{ userMessage: 'x' }is refused withTS2353 … 'userMessage' does not exist in type 'Pick<…>', on the same instrument that compiled a control line passingdetailsclean.Why it is the same class as #11719, and why it is a separate card
Identical shape — a channel
ApiErrorSchemadeclares, that the ONE shared writer cannot emit — and the same one-directional silence: nothing invalid ships, so no gate and no conformance suite notices, while the producer's deliberate text is gone and a consumer told to read the field finds nothing.It is separate because the two fields answer different rulings and have different producers.
declaredCodeis ADR-0112's demote (#9106 / #9232) and is produced byresolveThrownHttpError;userMessageis #9934's user-facing-text opt-in and is produced bydeclaredUserMessage, with a live sandbox-side reader inpackages/runtime/src/sandbox/quickjs-runner.ts. #11719's claim rulings scope that card todeclaredCodeexplicitly, so widening its PR to cover this would have been the scope breach those rulings exist to prevent.packages/rest/src/error-response.ts:558readsdeclaredUserMessage(error)for the flat/datadoor, so the same asymmetry #11719 records fordeclaredCode— flat door carries it, nested door cannot — applies here.Not established here
userMessagewas not censused. The types:sendError'sextracannot carrydeclaredCode, so every nested-envelope route silently drops an unregistered producer's own code spelling #11719 census found that six of the seven modules on the shared pair buildcode/messageas literals and never consult the shared resolver at all, so for those the field would be inert until they adopt it;packages/rest/src/package-routes.tsis the one that already holds a resolvedThrownHttpError(and therefore itsuserMessage) at thesendErrorcall.Pickagain or something that stops this recurring per field. Three ofApiError's optional fields are now known to be declared-but-unemittable-from-the-nested-writer in sequence, which may argue for a different shape than aPickextended one card at a time.Region:
packages/types/src/response-envelope.ts,sendError'sextraparameter.Generated by Claude Code