The Flower Shop order responses do not validate against the official spec schemas for the version they implement (2026-04-08, and unchanged under 2026-08-25): unset optional fields are serialized as null, where the schema types them as non-nullable.
This is the same defect #115/#117 fixed for checkout responses; the order route has the identical gap in three places.
Reproduction: fresh clone at main (00333a8), ucp-sdk pinned to 0.4.6 (a fresh uv sync currently resolves 0.5.0 and the server fails to import, tracked separately in #221/#222). Boot the server, run simple_happy_path_client.py to completion, then GET /orders/{id}. Validated the response with the official ucp-schema validator (root source/schemas/shopping/order.json, --op read --response) and independently with a Draft 2020-12 jsonschema referee over the full vendored schema set, against both the 2026-04-08 and 2026-08-25 spec corpora. All four combinations agree on the same 34 violating paths, faults of the form null is not of type ... (the capability extends fails its oneOf instead):
/label: null is not of type "string"
/line_items/0/item/image_url: null is not of type "string"
/line_items/0/quantity/original: null is not of type "integer"
/line_items/0/totals/0/display_text: null is not of type "string"
/line_items/0/parent_id: null is not of type "string"
/fulfillment/expectations/0/destination/extended_address: null is not of type "string"
/fulfillment/expectations/0/fulfillable_on: null is not of type "string"
/adjustments: null is not of type "array"
/totals/0/display_text: null is not of type "string"
/totals/0/lines: null is not of type "array"
/messages: null is not of type "array"
/attribution: null is not of type "object"
/ucp/services: null is not of type "object"
/ucp/payment_handlers: null is not of type "object"
/ucp/capabilities/dev.ucp.shopping.checkout/0/spec: null is not of type "string"
/ucp/capabilities/dev.ucp.shopping.checkout/0/extends: null is not valid under any of the schemas listed in the 'oneOf' keyword
...and 18 more of the same shape; the full list is in the linked PR.
Mechanism: three independent write sites model_dump an Order without exclude_none, so every unset optional field round-trips through storage (and back out) as an explicit null:
services/checkout_service.py:885, inside complete_checkout, the initial persist when an order is created.
routes/order.py:89, the PUT /orders/{id} update handler.
routes/ucp_implementation.py:321, order_event_webhook, the inbound receiver a partner posts order events to.
GET /orders/{id} and PUT /orders/{id} both read/write the same stored row, and the outbound order-event webhook the server sends (_notify_webhook in checkout_service.py) delivers that same stored row as its body, so all three surfaces carry the defect.
Why CI does not catch it: every existing order-touching test in integration_test.py (test_shipping_event_matches_order_schema, test_webhook_delivers_the_bare_order_as_body) validates with Order.model_validate(...). The generated pydantic model types every one of these fields Optional[...] = None, so it happily accepts a null the wire schema forbids. Pydantic model validation and JSON Schema validation disagree here in exactly the way the #115 issue body flagged for checkout.
Class sweep: checked every other order-adjacent response for the same pattern (checkout, cart, the order-event webhook body). Checkout is clean (fixed by #117). Cart is clean (built after #117, over its idiom, in #159). The webhook body is fixed by the same three sites above, since it reads the same storage. Table with both-validator results is in the linked PR.
Node twin: the Node reference server does not have this defect. src/api/order.ts returns the stored object via c.json(); the object is assembled in checkout.ts by copying fields straight through (e.g. parent_id: li.parent_id), and an unset optional field there is JS undefined, which JSON.stringify (and the Hono c.json) omits rather than serializing as null. Verified with an in-process probe driving the same create-complete-GET-PUT flow: 0 nulls in either the GET or PUT order body, both validated clean against both spec corpora. No fix needed on the Node side.
Expected: an unset optional field should be omitted from the response, the same as checkout and cart already do.
Fix: PR with exclude_none=True on all three sites, following the #117 idiom exactly, plus tests pinning the no-null invariant on all three surfaces.
The Flower Shop order responses do not validate against the official spec schemas for the version they implement (2026-04-08, and unchanged under 2026-08-25): unset optional fields are serialized as
null, where the schema types them as non-nullable.This is the same defect #115/#117 fixed for checkout responses; the order route has the identical gap in three places.
Reproduction: fresh clone at main (00333a8),
ucp-sdkpinned to0.4.6(a freshuv synccurrently resolves 0.5.0 and the server fails to import, tracked separately in #221/#222). Boot the server, runsimple_happy_path_client.pyto completion, thenGET /orders/{id}. Validated the response with the officialucp-schemavalidator (rootsource/schemas/shopping/order.json,--op read --response) and independently with a Draft 2020-12jsonschemareferee over the full vendored schema set, against both the 2026-04-08 and 2026-08-25 spec corpora. All four combinations agree on the same 34 violating paths, faults of the formnull is not of type ...(the capabilityextendsfails itsoneOfinstead):...and 18 more of the same shape; the full list is in the linked PR.
Mechanism: three independent write sites model_dump an
Orderwithoutexclude_none, so every unset optional field round-trips through storage (and back out) as an explicitnull:services/checkout_service.py:885, insidecomplete_checkout, the initial persist when an order is created.routes/order.py:89, thePUT /orders/{id}update handler.routes/ucp_implementation.py:321,order_event_webhook, the inbound receiver a partner posts order events to.GET /orders/{id}andPUT /orders/{id}both read/write the same stored row, and the outbound order-event webhook the server sends (_notify_webhookincheckout_service.py) delivers that same stored row as its body, so all three surfaces carry the defect.Why CI does not catch it: every existing order-touching test in
integration_test.py(test_shipping_event_matches_order_schema,test_webhook_delivers_the_bare_order_as_body) validates withOrder.model_validate(...). The generated pydantic model types every one of these fieldsOptional[...] = None, so it happily accepts anullthe wire schema forbids. Pydantic model validation and JSON Schema validation disagree here in exactly the way the #115 issue body flagged for checkout.Class sweep: checked every other order-adjacent response for the same pattern (checkout, cart, the order-event webhook body). Checkout is clean (fixed by #117). Cart is clean (built after #117, over its idiom, in #159). The webhook body is fixed by the same three sites above, since it reads the same storage. Table with both-validator results is in the linked PR.
Node twin: the Node reference server does not have this defect.
src/api/order.tsreturns the stored object viac.json(); the object is assembled incheckout.tsby copying fields straight through (e.g.parent_id: li.parent_id), and an unset optional field there is JSundefined, whichJSON.stringify(and the Honoc.json) omits rather than serializing asnull. Verified with an in-process probe driving the same create-complete-GET-PUT flow: 0 nulls in either the GET or PUT order body, both validated clean against both spec corpora. No fix needed on the Node side.Expected: an unset optional field should be omitted from the response, the same as checkout and cart already do.
Fix: PR with
exclude_none=Trueon all three sites, following the #117 idiom exactly, plus tests pinning the no-null invariant on all three surfaces.