feat: typed error responses and checkout conditional writes#628
Open
sakinaroufid wants to merge 4 commits into
Open
feat: typed error responses and checkout conditional writes#628sakinaroufid wants to merge 4 commits into
sakinaroufid wants to merge 4 commits into
Conversation
The service definitions only declared success responses, so anything generated from rest.openapi.json had no typed error shape despite the docs promising a full status code contract. Add reusable error response components carrying the standard envelope and declare 4XX/5XX on all 13 operations, an explicit 409 on the seven state-changing ones, and failed-delivery responses on the order event webhook. Also give search_catalog and lookup_catalog the same error_response oneOf branch every other operation already has, in both the REST and MCP definitions. Checkout also gets optimistic concurrency: businesses return a strong ETag on checkout responses, platforms echo it via If-Match on Update, Complete and Cancel, and a mismatch returns 412 Precondition Failed with the standard envelope so the platform can re-read, reconcile and retry. No schema changes; the tag rides standard HTTP fields and support is discovered from responses themselves.
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.
Description
TL;DR: UCP is built on agents consuming machine-readable contracts, and the discovery profile points them straight at
rest.openapi.json. But that file only modeled success responses, so every SDK, server stub and conformance test generated from it was blind to the error contract the docs promise, and error handling is exactly where autonomous agents need structure most. This PR declares the full error contract in the OpenAPI/OpenRPC definitions (4XX/5XXon all 13 operations,409on the state-changing ones, andsearch_catalog/lookup_catalogfinally able to return the standard error envelope like every other op), so generated clients get typed errors and conformance can test failure paths. It also addsETag/If-Matchconditional writes to checkout so two writers on one session, say an agent on the API and a buyer in a trusted UI, can't silently overwrite each other's changes. No schema changes anywhere.The REST service definition only declares the success response for each operation. The docs list a full status code contract (400 through 503, including 409 on idempotency-key reuse) with a JSON error body, but none of that exists in
rest.openapi.json, so anything generated from it (clients, server stubs, contract tests) has no idea errors exist. I hit this while generating a client: the documented 409 comes back as an unmodeled exception instead of something you can parse the error envelope out of.The first part fixes that. It adds reusable error response components carrying the standard envelope and wires them into every operation:
4XX/5XXon all 13 operations, an explicit409on the seven state-changing ones, and failed-delivery responses on the order event webhook. While in there I noticedsearch_catalogandlookup_catalogare the only two operations whose result has noerror_responsebranch, in both the REST and MCP definitions, so they can't return the standard envelope at all. Every other operation can, includingget_productin the same capability. Both results now use the sameoneOfpattern as everything else, and the behavior is documented on the catalog pages.The second part adds optimistic concurrency to checkout. Update Checkout is a full replacement, and a session can have more than one writer: an agent on the API plus a buyer parked in a trusted UI via
continue_url, or the business repricing things server side. Two writers that read the same state and write different replacements lose one of the updates silently. Idempotency-Key doesn't help, it only dedupes identical retries of one request.The fix is plain RFC 9110 conditional requests, no new schema fields. Businesses return a strong
ETagon checkout responses. Platforms echo it inIf-Matchon Update, Complete and Cancel. On a mismatch the business applies nothing and returns412 Precondition Failedwith the standard envelope (precondition_failed, severityrecoverable); the platform re-reads, reconciles and retries with the fresh tag.428 Precondition Requiredis there for businesses that want to force conditional writes. Support is discovered from responses: if a platform never saw a tag it just writes unconditionally, so existing implementations keep working untouched. Only REST gets a binding for now, since MCP/A2A/Embedded have no natural place to carry the tag.The conditional writes add normative behavior, so if that part needs to go through an enhancement proposal first I'm fine splitting it out into its own PR. The error declarations are just making the machine-readable contract match what the docs already say.
Category (Required)
Please select one or more categories that apply to this change.
ucp-schematool (resolver, linter, validator). (Requires Maintainer approval)Core Protocol covers the conditional writes; the error declarations are Capability plus Documentation.
Related Issues
None.
Checklist
!for breaking changes).The unchecked ones: the new conditional update examples (applied and rejected) are exercised by the docs example corpus rather than separate unit tests, and nothing under
source/schemas/changes. That's the point of putting the tag in HTTP fields, so there's also nothing to regenerate on the SDK side.Screenshots / Logs (if applicable)
mkdocs build --strictpasses in both site modes and the link checker comes back clean. The doc generation macros only read success responses, so rendered pages don't change apart from the added notes and the two new catalog entity sections.