chore(ci): update workflow path triggers and improve docs build#4
Open
sakinaroufid wants to merge 28 commits intomainfrom
Open
chore(ci): update workflow path triggers and improve docs build#4sakinaroufid wants to merge 28 commits intomainfrom
sakinaroufid wants to merge 28 commits intomainfrom
Conversation
Super-linter summary
All files and directories linted successfully For more information, see the Powered by Super-linter |
Super-linter summary
All files and directories linted successfully For more information, see the Powered by Super-linter |
bd97392 to
a322c67
Compare
Super-linter summary
All files and directories linted successfully For more information, see the Powered by Super-linter |
…ommerce-Protocol#233) * fix: Resolve broken assets when a space was in the name. * chore: Linter adjustments.
…-Protocol#210) * Adding currency field to top level of Order * Improving the currency description in Order * Update currency field description in source/schemas/shopping/order.json Co-authored-by: Ilya Grigorik <ilya@grigorik.com>
Super-linter summary
All files and directories linted successfully For more information, see the Powered by Super-linter |
…iversal-Commerce-Protocol#216) * ucp.status discriminator, unrecoverable severity - Add `status` field ("success"|"error") to ucp.json $defs/base as application-level operation discriminator (optional, default "success") - Constrain error_response.json to require ucp.status="error" via allOf - Add `unrecoverable` as fourth severity value to message_error.json - Remove bare 404 from GET /carts/{id} in rest.openapi.json (not_found is a business outcome returned as 200+error_response) - Fix all error examples across 7 doc files: add ucp.status:"error", add severity:"unrecoverable" to out_of_stock/not_found/version_unsupported - Fix checkout.md: add unrecoverable to severity table, correct phantom "severity: escalation" in business guidelines - Fix overview.md: version_unsupported examples now use error_response envelope shape Co-authored-by: Alex Park <alex.park@shopify.com> --------- * cosmetic cleanup Added severity guidance for error responses: SHOULD use recoverable or unrecoverable when no resource exists; requires_* severities assume a living resource. Cleanup: - Remove stale "Cart exists (200) or doesn't (404)" from cart.json - Strip trailing periods from message content examples - Wrap long lines in checkout.md and cart.md to ~80 chars * fix(errors): clarify resource state vs action Severity reflects two things: the resource state and the recommended action. The first three values (recoverable, requires_buyer_input, requires_buyer_review) apply when a resource exists — they differ on who acts. `unrecoverable` applies when no valid resource exists — the response contains ucp.status: "error" with no resource in the body. This is a clean partition: resource exists → severity tells you how to fix it; no resource → severity is unrecoverable, retry with new resource or inputs. Error responses MUST use severity: "unrecoverable". The transport/business error boundary follows the same principle: discovery failures are transport errors (inputs could not be retrieved or were malformed); negotiation and handler failures are business outcomes (handler executed on provided inputs and reported the result). * fix review feedback - Add continue_url hand off as platform action for unrecoverable severity in table, consistent with processing algorithm - Fix missing period between requires_buyer_review and unrecoverable descriptions in message_error.json schema --------- Co-authored-by: Ilya Grigorik <ilya@grigorik.com>
…otocol#169) Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.3.0 to 12.1.1. - [Release notes](https://github.com/python-pillow/Pillow/releases) - [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst) - [Commits](python-pillow/Pillow@11.3.0...12.1.1) --- updated-dependencies: - dependency-name: pillow dependency-version: 12.1.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Guillaume V. <4216770+ptiper@users.noreply.github.com> Co-authored-by: NanL3001 <nearlyforget2012@gmail.com> Co-authored-by: Ryan C <cheungr@google.com>
* fix/clarify version negotiation
Prior spec required businesses to process any request from a platform
at an older protocol version ("MUST process if platform ≤ business").
This imposed an unbounded backwards-compatibility obligation — every
business must support every historical version indefinitely.
New model: the business's protocol version is canonical. Businesses
declare accepted versions via a `supported_versions` array in their
profile (defaults to [version] when omitted). Platforms MUST support
the business's declared version to initiate a request. The business
validates the platform's version against its supported_versions list
and rejects with 422 if there is no match.
This also reclassifies version_unsupported from a negotiation result
(HTTP 200 with checkout message schema) to a protocol error (HTTP 422),
since version mismatch is a pre-negotiation gate check — the buyer
cannot resolve a protocol incompatibility.
* version selection step to capability intersection
The intersection algorithm matched capabilities by name only, without
specifying how versions are resolved when multiple are present. Add
explicit step: compute mutual version set, select highest, exclude
capability if no mutual version exists.
* version discovery: supported_versions URI map
Protocol and capability versions are independent axes — a flat profile
cannot express which capabilities are available at which protocol version.
supported_versions becomes a map from protocol version to profile URI,
where each URI points to a complete, self-contained profile for that
version. Platform bootstrap: fetch /.well-known/ucp, match version or
follow the URI in supported_versions, get an unambiguous capability set.
Key design choices:
- uri-reference format allows relative paths (/.well-known/ucp/2026-01-11)
- Current version served by the root profile; only older versions in map
- Version-specific profiles are leaf documents (no supported_versions)
- Both platform and business obligations explicit at version mismatch
* min_protocol_version for extension schemas
Extension schemas SHOULD declare min_protocol_version to indicate the
minimum UCP protocol version they require. This makes the protocol
dependency explicit and verifiable, rather than relying on profile
publishers to self-attest compatibility.
The schema author declares the requirement; the profile publisher
selects and advertises compatible versions. When present, platforms
and businesses SHOULD verify negotiated version >= min_protocol_version
during schema resolution. Incompatible extensions are excluded from
the active capability set with orphan re-pruning.
Changes:
- New "Protocol Version Constraint" subsection under Extension Schema
Pattern with field definition, example, and verification contract
- Resolution Flow gains step 4 (Protocol Compatibility) between
Schema Fetch and Compose
* address PR review feedback on version negotiation
- Clarify "current" → "the protocol version it declares"
- Upgrade SHOULD → MUST for min_protocol_version validation
- Cross-reference capabilities_incompatible error in request-time
validation flow
* version lifecycle is business policy
Protocol does not prescribe deprecation schedule.
* requires: unified version constraints
Replace min_protocol_version with a requires object that captures
both protocol and capability version constraints. Extension schemas
can now declare:
"requires": {
"protocol": { "min": "2026-01-23" },
"capabilities": {
"dev.ucp.shopping.checkout": { "min": "2026-06-01" }
}
}
Why capability version constraints: 3P extensions move on their own
release schedule. When com.acme.loyalty extends dev.ucp.shopping.checkout,
it depends on the semantic contract of a specific checkout version.
Structural schema validation (allOf composition) catches missing fields,
but cannot catch semantic changes — e.g., a field changing units, enum
values shifting meaning, or behavioral contracts tightening. The
extension author declaring "I understand checkout's contract as of
version X" is the only defense against this class of silent bugs.
Why min/max: extensions should be able to declare both a floor (the
oldest version whose semantics they depend on) and a ceiling (the
newest version they've verified against). An extension tested against
checkout 2026-01-23 through 2026-09-01 should not silently claim
compatibility with 2026-12-01 if a breaking change landed.
…ure (Universal-Commerce-Protocol#234) * chore: update CODEOWNERS with devops-maintainers for tooling and generated files * chore: update documentation ownership in CODEOWNERS * Update CODEOWNERS with hierarchical authority and Governance inclusion
…ry (Universal-Commerce-Protocol#55) * feat(catalog): Catalog capability for product discovery Introduces `dev.ucp.shopping.catalog` capability enabling platforms to search business product catalog and perform targeted product+variant lookups. Checkout capability assumes the platform already knows what item to buy (via variant ID). Catalog capability fills the discovery gap—enabling scenarios like "find me blue running shoes under $150" that lead to cart building and checkout. Product (catalog entry) ├─ id, title, description, url, category ├─ price: PriceRange (min/max across variants) ├─ media[]: images, videos, 3D models (first = featured) ├─ options[]: dimensions like Size, Color ├─ variants[]: purchasable SKUs (first = featured) ├─ rating: aggregate reviews └─ metadata: merchant-defined data Variant (purchasable SKU) ├─ id: used as item.id in checkout ├─ sku, barcode: inventory identifiers ├─ title: "Blue / Large" ├─ price: Price (amount + currency in minor units) ├─ availability: { available: bool } ├─ selected_options[]: option values for this variant ├─ media[], rating, tags, metadata └─ seller: optional marketplace context - Free-text query with semantic search support - Filters: category (string), price (min/max in minor units) - Context: country, region, postal_code, intent - Cursor-based pagination (default 10, max 25) - Accepts product ID OR variant ID - Always returns parent product with context - Product ID → variants MAY be representative set - Variant ID → variants contains only requested variant - NOT_FOUND returns HTTP 200 with error message (not 404) Location and market context unified into reusable types/context.json: { "country": "US", // ISO 3166-1 alpha-2 "region": "CA", // State/province "postal_code": "..." // ZIP/postal } Catalog extends with 'intent' for semantic search hints. REST: POST /catalog/search → search_catalog GET /catalog/item/{id} → get_catalog_item MCP (JSON-RPC): search_catalog get_catalog_item * Extract search filters to standalone schemas Inline object definitions in search_request.filters weren't rendered in generated docs (showed as plain "object" without properties). Fix by extracting to referenceable schemas: - search_filters.json: category + price filter definitions - price_filter.json: min/max integer bounds (distinct from price_range which uses full Price objects with currency) * add custom words for spellcheck * rebase on main * split into search and lookup capabilities - dev.ucp.shopping.catalog.search - dev.ucp.shopping.catalog.lookup Docs: - Restructure to catalog/ directory - index.md: shared concepts (Product, Variant, Price, Messages) - search.md, lookup.md: individual capability docs - rest.md, mcp.md: transport bindings * clarify context & market assignment * feat(context): add language field for content localization Add `language` to shared Context type for requesting localized content. Uses IETF BCP 47 language tags (same format as HTTP Accept-Language). Key design points: - For REST, platforms SHOULD fall back to Accept-Language header when field is absent; when provided, context.language overrides header - Same provisional hint pattern: businesses MAY return different language if requested language unavailable - Shared across catalog, checkout, cart (applies to buyer journey) * Add POST /catalog/lookup endpoint + context query params for GET Support both endpoints for consistency with cart/checkout: - GET /catalog/item/{id}?country=US®ion=CA&language=es - location context via query params, language overrides Accept-Language header - POST /catalog/lookup - full context in body (including sensitive `intent`) Context is extensible, but only a well-known subset (country, region, postal_code, language) is supported via GET query params. POST supports the complete context object. Also normalizes error codes to lowercase snake_case for consistency with checkout (NOT_FOUND → not_found, DELAYED_FULFILLMENT → delayed_fulfillment). * security hardening, currency support, schema refinements Context security (catalog, checkout, schema): - Clarify context signals are provisional hints—not authorization - Enforcement MUST occur at checkout with authoritative data - May be ignored if inconsistent with stronger signals (fraud rules, export controls, authenticated account) Currency support: - Add optional `currency` field to context for multi-currency markets - Supports scenarios like Switzerland (CHF default, EUR preference) - Added to REST GET query params and schema Security: - Add HTML sanitization guidance to product/variant descriptions - Platforms MUST strip scripts, event handlers, untrusted elements Schema refinements: - rating.json: add `scale_min` with default: 1 - price_filter.json: clarify context→currency determination - product.json: improve `handle` description for SEO vs API usage Consistency: - Error codes normalized to lowercase snake_case (not_found) - Fix invalid product example (missing description, price) - Update media/variants ordering language * fix linter issues * rename `price` fields on Product for clarity Product.price → Product.price_range Product.list_price → Product.list_price_range The fields reference PriceRange schema (min/max), so naming should reflect this. Variant.price and Variant.list_price remain unchanged as they reference single Price (not range). * extensible category schema with taxonomy support Introduce category.json schema to support multiple product taxonomies (google_product_category, shopify, merchant). Schema changes: - New: category.json with {value, taxonomy} structure - Product: category string → category[] (Category[]) - Variant: category string → category[] (Category[]) * refactor to POST-only batch lookup API Allow batch (multi ID) lookup support. Enables single and multi item lookup and aligns request and response shapes to search. Discussed at TC, agreed on POST-only: - Keeps request/response modeling symmetric (context in body) - Avoids special-casing query params for extensible context fields - Simplifies overall protocol surface - Single item GET can be added later if necessary Updated API: - REST: `POST /catalog/lookup` accepts `ids` array + `context` object - MCP: `catalog.ids` inside catalog object (matches search pattern) - Response returns `products` array (symmetric with search) Identifier flexibility: - MUST support product ID and variant ID - MAY support secondary identifiers (SKU, handle, etc.) - Secondary identifiers must be fields on returned product object - Client correlates results by matching fields (no guaranteed order) * Remove stale spec/ directory (schemas live in source/) * update category examples with multi-taxonomy response * fix examples to show price_range on Product * extract description into reusable type * feat: category search filter as an array Product.category is already an array of {value, taxonomy} objects, but the search filter only accepted a single string — making it impossible to filter by multiple categories in one request. Align the filter type with the product field: accept an array of strings with OR semantics. * feat: variant.input[] correlation for batch lookup Batch lookup accepts N identifiers and returns M grouped products. Without explicit correlation, clients must reverse-engineer which request identifiers mapped to which response variants by field-matching (comparing returned IDs, SKUs, handles against what was sent). This is brittle, ambiguous for secondary identifiers, and silent about match semantics. Add an `input` array to each variant in lookup responses. Each entry carries the originating request identifier (`id`, required) and an optional `match` type indicating resolution semantics: - Product ID in, featured variant out → match: "featured" (server selects a representative variant) - Variant ID in, exact variant out → match: "exact" (input directly identifies this variant) - Multiple inputs resolve to same product → one product, each matched variant carries its own input entries - Mixed product + variant IDs → both match types coexist in the same response --- SUPPORTED IDENTIFIERS R1. MUST support product ID and variant ID; MAY support secondary (SKU, handle) R2. Duplicate IDs → MUST deduplicate R3. One ID → multiple products: return matches, MAY limit result set R4. Multiple IDs → same product: MUST return once CLIENT CORRELATION R5. Response does not guarantee order R6. Each variant carries input[] identifying which request IDs resolved to it R7. Multiple IDs → same variant: one entry per ID, each with own match type R8. Variants without input[] MUST NOT appear in lookup responses RESOLUTION BEHAVIOR R9. exact: identifier resolved directly to this variant (variant-level) R10. featured: identifier resolved to parent product, server picked variant (product-level) --- A. Simple product ID — ["prod_abc"] R1 → product ID supported ✓ R10 → product-level resolution → featured R8 → variant must have input → 1 product, 1 variant variant.input: [{id: "prod_abc", match: "featured"}] B. Simple variant ID — ["var_xyz"] R1 → variant ID supported ✓ R9 → variant-level resolution → exact R8 → variant must have input → 1 product, 1 variant variant.input: [{id: "var_xyz", match: "exact"}] C. Two variant IDs, same product — ["var_abc_10", "var_abc_11"] R1 → variant IDs supported ✓ R4 → same product → returned once R9 → both variant-level → exact R8 → each variant must have input → 1 product, 2 variants var_abc_10.input: [{id: "var_abc_10", match: "exact"}] var_abc_11.input: [{id: "var_abc_11", match: "exact"}] D. Product ID + its own variant ID (overlap) — ["prod_abc", "var_abc_10"] R4 → same product → returned once R10 → prod_abc is product-level → featured variant R9 → var_abc_10 is variant-level → exact If featured ≠ var_abc_10: → 1 product, 2 variants featured.input: [{id: "prod_abc", match: "featured"}] var_abc_10.input: [{id: "var_abc_10", match: "exact"}] If featured = var_abc_10: R7 → multiple IDs resolve to same variant, one entry per ID → 1 product, 1 variant var_abc_10.input: [ {id: "var_abc_10", match: "exact"}, {id: "prod_abc", match: "featured"} ] E. SKU shared across products — ["SKU-SHARED"] R1 → SKU MAY be supported R3 → matches multiple products → return matches, MAY limit R9 → SKU resolves directly to variant → exact → N products (server's discretion on N), each with 1 variant each variant.input: [{id: "SKU-SHARED", match: "exact"}] F. Handle (product-level) — ["blue-runner-pro"] R1 → handle MAY be supported R10 → handle resolves to product → featured → 1 product, 1 variant variant.input: [{id: "blue-runner-pro", match: "featured"}] G. Duplicate identifiers — ["prod_abc", "prod_abc"] R2 → deduplicated to ["prod_abc"] → then Scenario A → 1 product, 1 variant variant.input: [{id: "prod_abc", match: "featured"}] H. Mixed batch — ["prod_abc", "var_xyz"] (different products) R1 → both supported ✓ R10 → prod_abc is product-level → featured R9 → var_xyz is variant-level → exact → 2 products prod_abc's variant.input: [{id: "prod_abc", match: "featured"}] var_xyz's variant.input: [{id: "var_xyz", match: "exact"}] I. Not found — ["prod_nonexistent"] R1 → product ID supported ✓ → no match → product not in response → response MAY include not_found message (per index.md) → 0 products, optional message * fix: update catalog to RFC 9421 signing parameters The signatures commit (0426800) replaced the single request_signature header with the RFC 9421 trio (Signature, Signature-Input, Content-Digest) across all endpoints. * fix: replace method_fields macros in MCP binding method_fields only parses OpenAPI format (paths → operationId). The catalog MCP binding was the only caller passing an OpenRPC file, which uses a different structure (methods[].name). This produced a build failure: "Operation ID search_catalog not found". Replace with extension_schema_fields pointing at the underlying JSON schemas directly, matching the pattern used by cart-mcp.md. * remove hard page-size cap; define clamp semantics Drop the maximum: 25 constraint on pagination.limit — businesses set their own upper bound. Add Page Size section to search.md defining the contract: limit is a request not a guarantee, implementations SHOULD accept at least 10, and MAY clamp silently when the request exceeds their maximum. * update context signal guidance * fix: replace enum with open string for `match` Strict enums in response schemas are a forward-compatibility trap: adding a new value breaks older clients whose validators reject unrecognized strings. match is server-controlled and will likely grow as resolution strategies evolve (e.g., fuzzy, semantic matching). Switch to open string with well-known values documented in description and examples array. Businesses can implement additional resolution strategies without requiring a schema version bump. * feat: unit_price for per-unit pricing Optional per-unit price comparison for products sold by weight, volume, or length (e.g., "$1.33 per 100ml"). Schema shape: amount + currency + measure + reference - measure: product quantity in packaging (e.g., 750ml bottle) - reference: display denominator (e.g., per 100ml, per 1kg) - amount: precomputed by business, not derived by platform Design decisions: - Business precomputes amount rather than platform deriving it - measure.value is number (fractional packaging, e.g., 0.75L), reference.value is integer (standard denominators are always whole: 1kg, 100ml, 1m) - Units are open vocabulary strings, because unit systems vary by product category * feat: availability.status qualifies available state available (bool) answers "can I buy this?" — today this covers in-stock, backorder, and preorder without distinguishing between them. status (string) adds the missing fulfillment contract: why is it available, and when will it ship? status qualifies available, not the other way around. A producer that only sets available without status is still valid — status is additive context, not a replacement. Consumers that don't recognize a status value fall back to the boolean. Open vocabulary (not enum) so new values extend without schema changes * feat: extensible search inputs Allow extensions to support image search, vector search, and other modalities without forcing validators to break when query is omitted. Remove required:["query"] from search_request. The base schema defines structural vocabulary; leaf schemas own behavioral contracts (e.g., require query, or accept query-or-like via anyOf). Add Search Inputs section: requests MUST include at least one recognized input, implementations define and enforce their own presence/content rules. * tighten pagination response constraints Make has_next_page required and enforce cursor presence via if/then when has_next_page is true. Keeps total_count and the pagination object itself optional. * extract reusable amount type w/ ISO 4217 guidance Monetary amounts were defined inline across 8 schemas with inconsistent descriptions (e.g., "minor (cents) currency units") that baked in a two-decimal assumption. Not all currencies use cents — ISO 4217 defines per-currency exponents: 2 for USD, 0 for JPY, 3 for KWD. Extracts types/amount.json as the single source of truth for the integer minor-unit representation with exponent guidance. All amount fields now $ref the shared type with local descriptions using consistent "in ISO 4217 minor units" phrasing. * barcode as typed array Replaces single `barcode` string with `barcodes` array of {type, value} objects supporting multiple barcode standards (UPC, EAN, ISBN, GTIN, JAN) * fix: pluralize array field names for consistency Every array field in UCP schemas uses plural names (tags, variants, barcodes, options, links, etc.) except category and input which slipped through as singular. Inconsistent naming forces implementers to memorize exceptions rather than rely on convention. Rename category→categories (product, variant, search_filters) and input→inputs (variant). All are new in this PR with no external consumers; non-breaking. * replace media enum with open string vocabulary Open string vocabulary preserves extensibility while the flat structure with additionalProperties lets vendors add type-specific fields without schema changes. Codified "prefer open vocabularies over enums" guidance in schema-authoring.md. * align REST+MCP examples with schema definitions Capability envelope in response examples used array-of-objects format but the schema defines an object-keyed-by-name registry pattern. Context examples used shorthand field names (country, region) that don't match the schema (address_country, address_region). * clarify filter-only browse as valid search input The search schema already accepts requests without query (no required array on search_request), but the normative language in search.md only listed query as a valid input, making filter-only browse technically non-conformant despite being listed as a core use case. Update Search Inputs section to explicitly list filters as a standalone input alongside query and extension-defined inputs. Filter-only requests represent browse operations where the business returns matching products without text-relevance ranking. * enforce inputs:required on lookup variants The prose MUST ("variants without inputs MUST NOT appear in lookup responses") was not enforced by schema. The inputs field lived on the shared variant type as optional, leaking a lookup-specific concern into search responses where it has no meaning. Move inputs out of shared variant.json and into a lookup_variant composition in catalog_lookup.json. The lookup response overrides product.variants.items to use the lookup-specific shape. Shared variant type is now capability-agnostic. * clarify independent adoption Search and lookup can be adopted independently. * define price filter currency contract Define waterfall: filter is denominated in context.currency; when presentment differs, business SHOULD convert; if conversion not supported, MAY ignore and SHOULD notify via message; when context.currency absent, denomination is ambiguous. Platforms SHOULD include context.currency when sending price filters.
…l-Commerce-Protocol#243) - Added MULTI_STATUS=false and ENABLE_GITHUB_PULL_REQUEST_SUMMARY_COMMENT=false to the Super-Linter environment block to prevent it from fatally crashing when the GITHUB_TOKEN is omitted. Co-authored-by: Ilya Grigorik <ilya@grigorik.com>
…sal-Commerce-Protocol#248) - Updated pyproject.toml to require mkdocs-material >=9.5.0 and regenerated uv.lock to resolve build errors related to the 'material.extensions.emoji' namespace which changed in version 9.4.
…-Protocol#226) * docs: add a link checker to validate all links and URLs * fix(docs): fix broken link anchors, paths, and absolute urls in code blocks on main * docs: resolve remaining absolute URLs and broken anchors on main * docs: parameterize hardcoded json versions in examples * ci: fix target branch condition for release link check - Explicitly targeted the 'Build and Verify Specification Docs' isolated build step to run on release branches only, rather than just any non-main branch. * ci: fix link checker resolving absolute paths in flat builds - Updated check_links.py to automatically strip version prefixes (e.g. 'latest', 'draft') from absolute URLs if the corresponding version directory doesn't exist locally, ensuring cross-version links are validated correctly against flat mkdocs builds during CI. * ci: handle SITE_URL subpaths in link checker - Updated check_links.py to correctly strip the configured SITE_URL subpath (e.g. '/ucp/') from the start of absolute links before resolving them against the local flat file structure, fixing false positive broken links when testing on forked repositories. * ci: fix ruff formatting for check_links.py * docs: natively generate checkout and error response anchors - Fixed broken anchor links for '#checkout' and '#error-response' in the 'draft' branch's checkout documentation. - Added explicit HTML anchor tags to the Capability Schema Definition heading. - Rendered the 'error_response.json' schema dictionary natively at the bottom of checkout.md to ensure downstream schemas that reference it can link to it successfully. * chore: fix openapi lint errors and schema versions - Restored a placeholder 'draft' version exclusively to OpenAPI and OpenRPC definitions to satisfy strict Spectral validation rules, while leaving standard JSON schemas cleanly unversioned to be dynamically injected by the build pipeline. - Abstracted duplicate absolute file references in 'handlers/tokenization/openapi.json' behind a local OpenAPI '#/components/schemas/' pointer. - Fixed Super-Linter CI pipeline failing to pick up the '.spectral.yaml' overrides by explicitly declaring the 'OPENAPI_CONFIG_FILE' environment variable in the workflow configuration, ensuring Spectral correctly ignores the 'oas3-valid-media-example' bug.
…#246) * feat(discount): extend discount capability to cart Discount previously only extended checkout, leaving cart without discount code support. This meant automatic discounts were invisible during cart exploration and agents couldn't show savings pre-checkout. - Add dev.ucp.shopping.cart extension in discount schema (create/update) - Update discovery example to show cart + checkout extends - Add cart-to-checkout continuity requirement (MUST carry forward codes) - Add cart discount example to specification docs * convert examples to Material content tabs Replace **Request:**/**Response:** markdown with === "Request"/=== "Response" tabbed format for consistency with cart-rest.md and checkout-rest.md. * clarify cart-to-checkout discount behavior Applied codes should be carried over and (re)evaluated at checkout time to preserve buyer context and continuity.
…ce-Protocol#260) Extract reverse_domain_name from ucp.json $defs into shopping/types/reverse_domain_name.json. Update all propertyNames refs in ucp.json to point to the standalone file directly. The docs renderer generates page-local anchors for $defs entries (prefixed with the parent schema name), but standalone types get rendered on the reference page with a linkable section. The $defs indirection caused broken #ucp-reverse-domain-name anchors on checkout, cart, catalog, and reference pages.
…tocol#262) Bumps [black](https://github.com/psf/black) from 26.1.0 to 26.3.1. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](psf/black@26.1.0...26.3.1) --- updated-dependencies: - dependency-name: black dependency-version: 26.3.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#203) * remove stale risk_signals references from spec Clean up all risk_signals language from the earlier draft that was superseded by the signals proposal (issue Universal-Commerce-Protocol#153). * feat: signals for authorization & abuse framework Platforms mediate buyer interaction, making them the sole party able to observe the transaction environment (IP, user agent, etc). Businesses need this data for authorization decisions, rate limiting, and abuse prevention — but have no direct access to it. - Signals are platform attestations: values MUST reflect direct observations, not relayed buyer claims - Signals serve authorization, rate limiting, and abuse prevention across cart and checkout - Signal feedback via info messages with well-known codes: risk, abuse - Proprietary signals can use reverse-domain naming (com.example.score) * enforce reverse-DNS on signal keys via propertyNames Unlike other UCP extension points — capability registries key by reverse-domain name, arrays keep items independent — signals is a shared flat map where multiple independent extensions contribute keys into the same object. Without naming discipline, collisions are inevitable: two extensions both defining "device_id" or "ja4" with different types or semantics produces silently unsatisfiable allOf composition. Reverse-domain naming makes every key self-describing: you know who defined it, what schema governs it, and collisions are structurally impossible. This is the same coordination-free mechanism UCP already uses for capabilities, services, and payment handlers. This commit enforces the MUST at the schema level via propertyNames (not just prose), renames well-known signals into the dev.ucp namespace (buyer_ip → dev.ucp.buyer_ip, user_agent → dev.ucp.user_agent), and updates all docs and examples to match. * broaden signals to include 3P attestations The original spec language required signals to be "direct platform observations," which excluded a valuable class of signals: independently verifiable third-party attestations (e.g., cryptographically signed results from an external verifier). These aren't buyer-asserted claims — the business can validate the signature against the provider's published key set without trusting the platform at all. * fix attestation example: self-verifiable shape Updated shape includes kid (RFC 7517 key selection/rotation), the complete signed payload (opaque, extension-defined), and sig over that payload. The business can now verify end-to-end: fetch JWKS, select key by kid, verify sig over payload. * simplify: single code, relax info-only Replace the risk/abuse code taxonomy with a single `signal` code. The path field already identifies what's being requested, and the signal is self-describing. Removes info-only language to allow and support different business strategies for information vs required signals. * echo back signals and context in responses Remove ucp_response: "omit" from signals (checkout, cart) and context (checkout). The business now echoes these back like any other field. The only remaining response-omit in shopping schemas is token_credential.token, which is correctly omit'd (sensitive payment data that must not leak). * extend signals to catalog Add signals to catalog search and lookup requests. Drop sibling description overrides from all $ref sites (checkout, cart, catalog_search, catalog_lookup) — signals.json owns its description, consumers just $ref it. Also drop "Transaction" from the description since signals now span non-transactional operations (catalog search). * deprecate risk_signals with transition annotation The old risk_signals field was removed outright in an earlier commit, this adds risk_signals back to checkout schema with deprecated: true and a transition from optional to omit on the complete operation, signaling removal in the next version.
…Protocol#250) * feat: eligibility claims & verification contract Introduce `context.eligibility` — buyer claims about eligible benefits (loyalty membership, payment instrument perks, etc.) that Businesses can act on across the shopping lifecycle. Processing model: - Platform provides claims via context.eligibility on any request - Business MAY act on recognized claims (adjust pricing, product access, provisional discounts); MUST ignore unrecognized claims - At checkout completion, all claims that influenced the checkout MUST be resolved: verified against proof, or rescinded by Platform - Unresolved claims block completion (invalid_eligibility error) - Business MUST NOT mutate checkout on verification failure Layering: - context.json: eligibility array with reverse-domain $ref validation - checkout.md: normative verification contract (core obligation) - discount.json: provisional + eligibility fields on applied_discount for structured attribution when discount extension is active - catalog/index.md: MAY adjust price/list_price for eligible claims - error_code.json: invalid_eligibility standard error Key design decision: eligibility lives on context (not PaymentInstrument) enabling full-funnel coverage from catalog through checkout. Verification is a core checkout concern; the discount extension adds attribution. * align error code to noun_adjective pattern * fix: address PR Universal-Commerce-Protocol#250 review feedback Terminology: - "claims that influenced the checkout" → "accepted claims" throughout, aligning with the accepted/not-accepted partition (maximenajim, ACSchil) - "not applied" → "not accepted" for consistency Verification semantics: - Clarify that verification failure MUST only affect the messages array, not checkout state (line items, totals, discounts, etc.) (maximenajim) - Add path field to eligibility_invalid example for machine-readable partial failure identification (ACSchil) Messages contract: - Add MAY use type: "info" to explain effects of accepted claims, complementing SHOULD warn on rejection (maximenajim) Catalog: - Add non-binding pricing contract to "Relationship to Checkout" section: catalog responses are not transactional commitments, checkout is authoritative, responses SHOULD NOT be reused across sessions without re-validation (maximenajim) * add eligibility message codes for warning and info Standardize codes for the three eligibility message types: eligibility_not_accepted (warning), eligibility_accepted (info), and eligibility_invalid (error at completion). * remove display MUST from provisional Per TC discussion, this is already captured via messages flow. --------- Co-authored-by: Alex Schillinger <alexcschillinger@gmail.com>
ucp.dev site building cleanup: - Stop setting latest alias in build_local.sh script, as it was updating latest alias to draft - Add a symlink for /specifications/ to /latest/specifications/ - Update pages build for main to delete old files, and preserve version dirs
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
Improves existing CI workflows:
linter.yaml:pathsfilter so the linter workflow only triggers on relevant file changes instead of every PRFILTER_REGEX_EXCLUDEto multiline YAML for readabilitydocs.yml:requirements-docs.txttopyproject.toml+uv.lockuv synctouv sync --all-groupsto install all dependency groupsdtolnay/rust-toolchain@stable) and cargo caching (Swatinem/rust-cache@v2) before the existingcargo install ucp-schemastepOther:
EDITMSGto.cspell/custom-words.txt.pre-commit-config.yaml(prettier hook)This is part 3 of 3 from Universal-Commerce-Protocol#13, split per maintainer request.
Type of change
Checklist