feat(ir)!: make ErrorCase a response: name, headers, media types - #438
Open
fuad-daoud wants to merge 1 commit into
Open
feat(ir)!: make ErrorCase a response: name, headers, media types#438fuad-daoud wants to merge 1 commit into
fuad-daoud wants to merge 1 commit into
Conversation
ir.ErrorCase and ir.Response are two lowerings of one Response Object, but
only one of them could say what the source declared. Response carries a
Name whose Hint is the status spelling, Headers, and a Payload holding
every media type; ErrorCase carried none of the three — no name at all, no
headers, and one bare TypeRef where the content map belongs.
Everything that fell outside those fields went to ErrorCase.Unmodeled with
an info diagnostic, so a consumer's behaviour changed with the status
class and nothing said so:
- Retry-After and the rate-limit family live on 429 and 503, precisely the
side with no typed home for a header.
- A 4xx declaring application/json and application/problem+json kept the
first schema and lost the media-type key entirely; a 4xx declaring one
media type lost the key it was written under.
- "5XX" and "default" had no faithful round-trip: StatusRange renders
{500,599} and {0,0} with no record of how the source spelled them.
ErrorCase now has Name Naming, Headers []Property and Payload *Payload in
place of Type, each spelled as Response spells it, and the error path
lowers through the same responseName, lowerHeaders and lowerPayload the
success path uses. preserveErrorHeaders, fillErrorType, preserveErrorContent
and errorContentMessage existed only to soften this gap and are gone with
it, along with the two info diagnostics they emitted.
pass.checkEncodingKeys grows a fourth Payload carrier, reached at both
positions an ErrorCase hangs from — an operation's Errors and a service's
CommonErrors — since a check walking only the first would resolve a
service-level error's encoding keys against nothing in silence.
BREAKING CHANGE: ErrorCase.Type is removed; an error case's models are its
Payload.Contents entries' types. The JSON gains "name", "payload" and
"headers" and loses "type". ir.IRVersion is deliberately not moved here:
per ir-design.md §2.1 a line of work bumps it once, where it lands on main,
and two earlier shape changes on this branch left it alone for the same
reason.
The normative rows in docs/ir-design.md that described the old behaviour
are updated, as is the error-taxonomy example in docs/emitter-design.md.
The per-status-errors conformance fixture gains a 429 declaring two media
types and two rate-limit headers, which is what makes the new fields
witnessed rather than merely present.
Closes #422
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYeBgDsskwnyitLgPGCPn1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #422. Breaking, and the largest change in the stack.
ir.ErrorCaseandir.Responsewere not siblings. Responses split by status — 2xx/3xx toResponse, 4xx/5xx/defaulttoErrorCase— and the two nodes were shaped differently:ResponseErrorCasebeforeName.Hint="200"Headers []PropertyUnmodeledTypeRef, the rest dumpedSo anything a consumer did with responses behaved differently by status class, silently.
Retry-Afterand rate-limit headers live on 429 and 503 — precisely the side with no typed home. A 4xx declaring two media types kept one and lost the media-type key entirely.703 occurrences of "error response media type has no ErrorCase home" across GitHub and Stripe.
ErrorCasenow lowers through the same helpers as the success path —responseName,lowerHeaders,lowerPayload. Four functions that existed only because of the gap are deleted, and with them two info diagnostics and theopenapi:headers/openapi:contentwrites.pass/validate.gogrows the fourthPayloadcarrier, reached at both positions anErrorCasehangs from —Operation.ErrorsandService.CommonErrors. The completeness guardTestEncodingCarriers_NameEveryPayloadFieldInTheIRfired on its own and named it.The conformance fixture gained a 429 declaring two media types and two rate-limit headers, so the new fields are witnessed, not merely present — deleting either reddens the case.
BREAKING CHANGE:
ErrorCase.typeis removed;name,payloadandheaderstake its place. A 0.3.0 consumer finds notypeon an error case and cannot reach its models at all.Stack 4 of 8. Base
stack/3-ir-fields— review and merge bottom-up. Every commit here passedmake gatewhen it landed, and the full gate was re-run on the top of the stack. Run it asGOTOOLCHAIN=go1.26.3 make gate; this machine's Go 1.27 fails it for reasons unrelated to any change (#431).🤖 Generated with Claude Code
https://claude.ai/code/session_01SYeBgDsskwnyitLgPGCPn1