Skip to content

Proposal: re-validate the cart at checkout and relay a handoff refusal - #20

Draft
vishkaty wants to merge 1 commit into
anthropics:mainfrom
vishkaty:proposal/checkout-revalidation
Draft

vishkaty wants to merge 1 commit into
anthropics:mainfrom
vishkaty:proposal/checkout-revalidation

Conversation

@vishkaty

@vishkaty vishkaty commented Sep 10, 2026 •

Copy link
Copy Markdown

Reference implementation available (2026-09-18): the implementation this note describes is published in https://github.com/vishkaty/commerce-agents-medusa (a Medusa v2 backend and host), checked by https://github.com/vishkaty/commerce-agents-conformance. Issues and pull requests are taken there; this draft stays as the design note.

Proposal: cart re-validation at checkout

The gap

checkout renders the cart the backend returns and puts checkout_handoff's URL on the
card. Between the card and the payment, a line can sell out or move in price; on a live
platform (Medusa v2) a cart line keeps its add-time price after the merchant reprices,
so the card and the hosted checkout charge a stale price. The contract says nothing
about re-validation and no example backend re-validates. When a handoff does raise
Unavailable, the executor relays it (domain_error in shopping_agent/executor.py),
but with the add-to-cart wording, "Nothing was added: … add that only once they choose
it", which misdescribes a checkout refused because the cart went stale.

What this adds

  1. docs/backends.md, checkout_handoff: "re-validate every line against the live
    catalog before returning a URL; a line that can no longer be fulfilled raises
    Unavailable naming it; a line whose price moved is refreshed and reported so the
    next checkout shows what will be charged."
  2. enrich_checkout catches Unavailable from the handoff and raises
    PresentationRefused with its message, so the refusal is worded for a checkout (the
    cart, not an add).
  3. The host side: complete a paid cart only when the amount the provider collected
    matches the cart's total at completion; otherwise hold it for review.

Reference implementation

Done over Medusa v2 in a lab adapter: _revalidate (sold out, short, repriced), a
CartStale(Unavailable, ValueError), which run_presentation relays with its own
wording today, and a host that holds a mismatched payment. Verified live with a repricing between add and pay.

Correction, 2026-09-18. An earlier version of this note said the executor reports a
handoff Unavailable as an outage. It does not: BaseToolExecutor.execute passes it
through domain_error, which relays it with the add-to-cart wording (checked by running
checkout at fd4d592 over a backend whose handoff raises Unavailable). The review in
SanHsien's fork (docs/DECISIONS.md there) caught it; the design note is corrected above.

Opened as a draft: this is a design note for discussion before code is written against the interface. A reference implementation exists and can follow as a pull request in the agreed shape.

@vishkaty
vishkaty force-pushed the proposal/checkout-revalidation branch from b3cfe06 to 8b7f9b9 Compare September 18, 2026 13:15
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.

2 participants