Skip to content

docs: align the published quote contract with what the code serves - #237

Open
Sebastian Hölscher (HoelShare) wants to merge 1 commit into
mainfrom
claude/quote-contract-drift
Open

Sebastian Hölscher (HoelShare) wants to merge 1 commit into
mainfrom
claude/quote-contract-drift

Conversation

@HoelShare

Copy link
Copy Markdown
Contributor

Summary

The com.shopware.quote spec and openapi had drifted from the code. Only Quote's wire shape is test-pinned, so I re-read both end to end against src/Bridge, src/Ucp and the UCP SDK 0.0.6. These are the fixes, each checked against the code path. The only PHP changes are two docblocks.

From the 2026-10-09 docs audit

  • Endpoint: the descriptor publishes only version, spec and schema. The spec no longer shows a config block. Agents take the operation paths from the schema's paths and resolve them against the shop base URL, which is how scripts/ucp-quote-agent.py already works. I fixed the spec rather than publishing an endpoint: nothing reads one.
  • Version: kept at 2026-04-08. Since 0.0.6 the SDK negotiates capabilities by exact version, and calls them "contract revisions, not release numbers" (NegotiatedCapabilityResolver). Bumping it with no contract change would silently drop the capability for agents that list the current version. The spec's Versioning section now says this, and so does a docblock on QuoteCapabilityDescriptor::VERSION.
  • 404 vs 501: without SwagCommercial's quote bundle, AgentFacingRoutes never imports UcpQuoteController, so every call is a 404. 501 means installed but unlicensed, or missing a service the capability needs. The spec, openapi info, the Unsupported/NotFound responses and the QuoteCapability docblock now say this. requestQuote/listQuotes also list the 404. Nothing returns 501 for a "disabled" capability, so that claim is gone.
  • Minor: in the spec's state table, expired is now the merchant's turn, matching x-state-machine. The openapi now reads "Where they are absent". The spec lists the 409 for a stored response too large to replay.

Found on the end-to-end re-read

  • Error code examples now match what the SDK emits: invalid_request, not_found, idempotency_conflict, capability_unsupported.
  • The 403 for a missing scope and the accept 400 both arrive as invalid_request. The detail is in content (prefix insufficient_scope:, or the quote-to-order reason). CHECKOUT__QUOTE_CANNOT_PLACE_ORDER never reaches the body.
  • The authorization-request 401 does not name UCP-Agent, because the SDK error response drops WWW-Authenticate. The errors table now quotes what the message says.
  • The Quote Management customer feature gates only request, counter, accept and decline. Reads are not checked.
  • order appears only in the accept response and its replay. a2cn_session_id appears only in the request response. ucp.capabilities never appears on quote responses: the SDK maps no quote.* operation.
  • A key still being processed is also a 409. A non-integer limit/page is a 400. Idempotency-Key is "required by shops configured to demand it", no longer tied to the signature policy.

Test plan

  • composer run test: 1923 tests pass. QuoteSnapshotTest and QuoteContractControllerTest read the openapi.
  • composer run quality: exit 0
  • openapi parses as JSON

🤖 Generated with Claude Code

The spec and openapi described behaviour the plugin does not have:

- The descriptor carries no `config.endpoint`; operation paths come from
  the schema's `paths`, resolved against the shop base URL.
- The capability version is the contract revision, not the UCP protocol
  version. SDK negotiation matches capabilities by exact version, so it
  stays 2026-04-08.
- Without SwagCommercial's quote bundle the quote routes are never
  imported, so every call is a 404; 501 is the installed-but-unlicensed
  case. Nothing answers 501 for a "disabled" capability.
- `expired` is the merchant's turn (it can extend it); the openapi
  per-line-ask note read "where they are" for "where they are absent";
  the spec now lists every idempotency 409, including in-flight keys.
- Error `code` values are the SDK's (`invalid_request`, `not_found`,
  `capability_unsupported`, `idempotency_conflict`); insufficient_scope
  and the accept failure arrive as `invalid_request` with the detail in
  `content`; the UCP-Agent 401 message does not name the header.
- The Quote Management feature gates only the four mutating operations.
- `order` and `a2cn_session_id` appear only in the accept and request
  responses; quote responses never carry `ucp.capabilities`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant