Skip to content

feat: add negotiated fulfillment location context - #671

Open
igrigorik wants to merge 2 commits into
mainfrom
feat/location-context-contract
Open

feat: add negotiated fulfillment location context#671
igrigorik wants to merge 2 commits into
mainfrom
feat/location-context-contract

Conversation

@igrigorik

Copy link
Copy Markdown
Contributor

This PR supersedes #642, which identified the need to preserve the Business location behind store-scoped Catalog results through Cart and Checkout. Instead of adding #642's raw location field to base Context, this PR makes support explicit through Fulfillment.

  • adds optional context.location to Fulfillment's Catalog, Cart, and Checkout projections;
  • gives Cart location-aware Context without full Fulfillment structures;
  • introduces one opaque, stable, Business-scoped Location Identifier;
  • defines field roles, cross-Business isolation, unknown-ID handling, and precedence.

Adding a Business-defined location ID to base Context would define its syntax without advertising which Businesses recognize it, which operations consume it, or what behavior it affects. Fulfillment owns the relevant surfaces: Catalog methods report resolved locations, Catalog filters name destinations, and Checkout exposes destinations and selections. Contributing context.location through Fulfillment lets parties negotiate each parent capability with extends.

Contract

The same Location Identifier can appear in several fields without giving them the same meaning:

Field Meaning
context.location Provisional, non-binding anchor for location-scoped pricing, availability, estimates, or initial options.
Catalog filters.fulfills_to Explicit destination filter: can this product be fulfilled to here?
variants[].fulfillment.methods[].location Location for which a place-based Catalog method was resolved.
Checkout selection or authoritative address Explicit destination for that scope; supersedes context.location.

Journey example...

Catalog reports method.location = loc_123
        ↓
Cart carries context.location = loc_123
        ↓
Cart-to-Checkout preserves context.location
        ↓
Checkout offers loc_123 as an initial choice
        ↓
Platform selects selected_destination_id = loc_123
  → loc_123 becomes explicit for that fulfillment scope

The new common/types/location_id.json reuses the existing string wire type across Fulfillment context, Catalog method locations, destination filters, and retail locations. Within one Business, the same value identifies the same location; its containing field determines its role. selected_destination_id stays generic because it can select other destination types.


Checklist

  • Capability: New schemas (Discovery, Cart, etc.) or extensions.
  • Documentation: Updates to README, or documentations regarding schema or capabilities.
  • I have followed the Contributing Guide (including Conventional Commits title requirements and ! for breaking changes).
  • I have updated the documentation (if applicable).
  • My changes pass all local linting and formatting checks.

   Store-scoped Catalog results had no advertised way to preserve the
   Business location through Cart and into Checkout. Defining the field on
   base Context would make every Shopping implementation accept an opaque
   Business-scoped identifier without advertising the behavior that produces
   or consumes it.

   Have Fulfillment contribute `context.location` to Catalog requests, Cart,
   and Checkout through explicit parent composition. Cart receives only the
   location context, and implementations supporting both Catalog and Cart
   should extend both to preserve continuity. For example, `loc_123` can
   scope Catalog availability, continue through Cart, and become explicit
   only when selected as a Checkout destination.

   Introduce a shared Location Identifier and reuse it for Fulfillment
   context, Catalog method locations, destination filters, and retail
   locations. This preserves one identity across surfaces while each
   containing field defines its role. Keep `selected_destination_id` generic
   because it may also reference non-location destinations.
@igrigorik igrigorik added this to the Working Draft milestone Aug 1, 2026
@igrigorik
igrigorik requested a review from jingyli August 1, 2026 05:00
@igrigorik igrigorik self-assigned this Aug 1, 2026
@igrigorik igrigorik added the TC review Ready for TC review label Aug 1, 2026
@jingyli

jingyli commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Thanks @igrigorik! Adding a general thought for further discussion here:

While I agree that we should have a unified way of representing the explicit Location identifier when it comes to binding-contracts with respect to fulfillment (examples 2, 3, 4 in the table above), I'm wondering if grouping context.location (a provisional, non-binding hint shared on behalf of the user) here as well is too constraining, especially if we look at use cases outside of Shopping vertical that may materialize in the future?

An example:

  • A user is looking to browse room availability or make a booking at a specific hotel branch.
  • Business may require a location hint provided to establish the session (this ID may or may not come from the common Location capability we are looking to introduce in feat!: Introduce Location Search + Lookup capabilities #589).
  • However, lodging is an industry where there is no concept of physical fulfillment.
  • We are then forced into picking one of the 2 options below:
    1. Adopt a consistent pattern where context.location is exposed and negotiated via a dummy "fulfillment" capability. (con: semantic irrelevance)
    2. Model its own bespoke location_id in context. (con: fragmentation)

@jamesandersen

Copy link
Copy Markdown
Contributor

Thanks @igrigorik — this answers the concern I raised on #642, where I'd pushed on context.location being an "identifier or name hint." The new common/types/location_id.json + "Location Identifier" glossary term make it a stable, well-defined ID (also capturing @gsmith85's first-class-concept suggestion). On the binding fields — method location, fulfills_to, selected_destination_id — this is a clear win.

On @jingyli's point about context.location being provisional and cross-vertical: a provisional location signal on base Context isn't actually novel. shopping/types/context.json already allOfs common/types/locality.json, so Context carries coarse address_country/address_region/postal_code today — framed as "provisional buyer signals… not authoritative data." The "base Context advertises nothing about who honors it" concern applies equally to locality, yet it works fine under the tolerant-reader contract — which suggests the objection is really about binding fields, not provisional ones.

So the narrow question: does an opaque location ID add provisional value beyond the coarse locality Context already carries? If yes (branch-level granularity, e.g. the hotel case), it may belong alongside locality on Context rather than scoped under Fulfillment — which is what makes it feel stranded for non-fulfillment verticals. If no, coarse locality already establishes the session and the branch-specific choice looks more like a binding selection. Worth noting the "non-binding" description sits in tension with the lodging example's Business that requires the hint — resolving that probably resolves the placement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants