Skip to content

feat: Introduce Location Search + Lookup capabilities#589

Open
jingyli wants to merge 14 commits into
mainfrom
feat/location
Open

feat: Introduce Location Search + Lookup capabilities#589
jingyli wants to merge 14 commits into
mainfrom
feat/location

Conversation

@jingyli

@jingyli jingyli commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Description

A mirror copy of #545 that supersedes it.

Defines standard interfaces for discovering, searching, and retrieving physical locations (e.g., retail stores, restaurants, warehouses, lodging properties).

It introduces two new capabilities under the dev.ucp.common namespace (consistent with Shopping's Catalog capability design):

  1. Location Search (dev.ucp.common.location.search): Discovery-focused endpoint for natural language query, geographic, and offerings-based filters.
  2. Location Lookup (dev.ucp.common.location.lookup): Resolution-focused endpoints supporting single & batch lookups.

Some key commerce flows it will be able to unlock:

  • Local Pickup Discovery: Finding locations like retail stores or restaurant branches
    nearby that support customer pickup and checking their operating hours & inventory availability
    before selection.
  • Fulfillment Area Verification: Checking if a specific location (e.g., utility depot, restaurant,
    or local service provider) has delivery coverage for a buyer's address.

Category (Required)

  • Core Protocol: Changes to the base communication layer, global context, or breaking refactors. (Requires Technical Council approval)
  • Governance/Contributing: Updates to GOVERNANCE.md, CONTRIBUTING.md, or CODEOWNERS. (Requires Governance Council approval)
  • Capability: New schemas (Discovery, Cart, etc.) or extensions. (Requires Maintainer approval)
  • Documentation: Updates to README, or documentations regarding schema or capabilities. (Requires Maintainer approval)
  • Infrastructure: CI/CD, Linters, or build scripts. (Requires DevOps Maintainer approval)
  • Maintenance: Version bumps, lockfile updates, or minor bug fixes. (Requires DevOps Maintainer approval)
  • SDK: Language-specific SDK updates and releases. (Requires DevOps Maintainer approval)
  • Samples / Conformance: Maintaining samples and the conformance suite. (Requires Maintainer approval)
  • UCP Schema: Changes to the ucp-schema tool (resolver, linter, validator). (Requires Maintainer approval)
  • Community Health (.github): Updates to templates, workflows, or org-level configs. (Requires DevOps Maintainer approval)

Related Issues

This is related to RFC #375's section 10.

Checklist

  • 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.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • (For Core/Capability) I have included/updated the relevant JSON schemas.
  • I have regenerated Python Pydantic models by running generate_models.sh under python_sdk.

Screenshots / Logs (if applicable)

location_ucp_doc

@igrigorik igrigorik left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, the shape and proposal make sense—ty for scaffolding this. A few design questions I'd like to work through...

1. Should common be the service, or the namespace containing a Location service?

The capability names are scoped to Location:

dev.ucp.common.location.search
dev.ucp.common.location.lookup

The profile examples, however, advertise the transport as:

{
  "services": {
    "dev.ucp.common": [{ "...": "..." }]
  }
}

I agree with dev.ucp.common.* as the namespace for cross-vertical primitives. However, I'm not sure about making dev.ucp.common one catch-all service: every future shared capability would then share an endpoint, transport schema, etc.

The alternative split is:

namespace:    dev.ucp.common.*
service:      dev.ucp.common.location
capabilities: dev.ucp.common.location.search
              dev.ucp.common.location.lookup

Do we think a catch-all .common service is preferred to scoped services?

2. Is geofence_radius sufficient to model the domain?

The current geo object combines a physical point with a circular geofence_radius, while geofence_point asks whether a requested point falls inside that circle. This covers a useful simple case, but in my experience service areas are often irregular polygons, disjoint regions, postal-code unions, etc.

Do we need to model (thin, hopefully) RFC 7946-based service-area shape supporting Polygon / MultiPolygon?

3. Can we adopt a Schema.org-inspired shape instead of inventing a bespoke one?

The latest changes make the custom model deterministic by defining 24:00, precedence between is_closed / is_24_hours / intervals, and a two-entry convention for overnight hours. That is an improvement, but it also highlights how much new protocol syntax and interpretation logic we are defining.

Schema.org's OpeningHoursSpecification already provides the core semantics we need:

  • dayOfWeek, opens, and closes;
  • multiple entries for split shifts;
  • closes < opens means the interval spans the next day;
  • no opens means closed;
  • validFrom / validThrough cover date-specific exceptions.

A Schema.org-inspired UCP shape could look like:

{
  "timezone": "America/New_York",
  "hours": [
    { "day_of_week": "monday", "opens": "09:00", "closes": "17:00" },
    { "day_of_week": "monday", "opens": "18:00", "closes": "22:00" },
    { "day_of_week": "friday", "opens": "22:00", "closes": "02:00" }
  ],
  "exception_hours": [
    {
      "valid_from": "2026-12-24",
      "valid_through": "2026-12-24",
      "opens": "09:00",
      "closes": "14:00"
    },
    {
      "valid_from": "2026-12-25",
      "valid_through": "2026-12-25"
    }
  ]
}

The contract is compact: each entry describes one open interval; repeated entries for the same day represent split shifts; a closes value earlier than opens spans midnight; and a date-bounded exception without opens means closed for that period. timezone supplies the interpretation context for local times. The example adapts Schema.org's semantics to UCP's snake_case naming and HH:MM convention rather than copying its JSON-LD representation verbatim.

Is there a concrete requirement that OpeningHoursSpecification cannot model? If not, I would align to it and remove the custom closed/24-hour flags, precedence rules, and overnight splitting convention.

4. Is offerings.inventory.quantity an inventory-disclosure query?

The store-finder use case—"which nearby location can fulfill the item I need?"—is valuable. A hard minimum quantity filter, however, lets a caller repeatedly probe thresholds and approximate a store's stock level even when the response never returns a count.

I suggest framing this as Buyer demand and coarse availability, not inventory disclosure:

  • reuse UCP's existing availability semantics rather than parallel inventory concept;
  • treat requested quantity as intent that a Business may coarsen;
  • change language to not imply that exact on-hand counts are returned;

5. What does offerings mean? 😅

Modelled request can filter on:

{
  "offerings": {
    "amenities": ["..."],
    "inventory": [{ "id": "..." }]
  }
}

The standardized Location entity, however, exposes neither amenities nor offerings. Although the filter contract implies that each returned Location satisfies the requested values, a Platform cannot render the matched facts, inspect additional amenities, or present structured item availability from the response without relying on vendor-defined fields. There is also a category question: amenities are relatively static Location characteristics, while item availability is dynamic and tied to another Catalog or menu identity. Grouping both under offerings may be convenient structurally, but does not necessarily make them one semantic concept.

My inclination would be to put static amenities on Location, model dynamic availability separately using the existing UCP availability vocabulary, and flatten the filter unless offerings gains a clearer cross-vertical contract.

6. What amenity vocabulary is interoperable across Businesses and verticals?

An open string is the right wire type for UCP, but the current proposal defines no well-known values. Should UCP publish a small open vocabulary for interoperable matching, and which initial values are important enough to standardize?

7. What are the capability-specific security and privacy requirements?

Location handles Buyer location hints, enumerates physical sites, describes service coverage, and can be used to probe per-location item availability. The generic Signals reference and current privacy note do not cover those capability-specific risks.

I think the specification needs explicit Security and Privacy Considerations covering:

  • coarse-by-default Buyer location and progressive disclosure;
  • purpose limitation and retention of location inputs;
  • store/site enumeration and rate limiting;
  • inventory and availability probing;
  • disclosure of internal, private, or non-buyer-visible locations;
  • disclosure of precise service-area geometry.

8. What is the bounded Location projection for Catalog and Checkout?

This PR points Checkout’s pickup destination directly at the rich common Location entity. That couples Checkout to the complete Location discovery model: geo, hours, service area, amenities, and every future Location extension would automatically accrete into the transactional response. I don't think that's right.

In Catalog Fulfillment (#507), we deliberately introduced a thin Location projection: a stable location ID plus method description. This avoided embedding an N-store matrix in product results and deferred richer Location facts to a separately negotiated capability. That boundary still makes sense, but I think there is a middle ground: split Location into a bounded base and an extended discovery entity.

Base Location
  id
  name
  address?
      │
      └── allOf → Extended Location
                    geo
                    hours
                    exception_hours
                    timezone
                    amenities
                    service_area

Catalog and Checkout would use the bounded, buyer-renderable base. Location Search/Lookup would return the extended entity when the service is negotiated. Platform requests would select a Location by stable id rather than asserting Business-owned name/address facts. This preserves the bounded Catalog model from #507, keeps Catalog and Checkout independently renderable, and prevents the full Location discovery model from accreting into every Checkout response.

Location search intentionally permits Business-defined filters, but the schema
relied on JSON Schema's implicit open-object default while the prose named
additionalProperties as the extension mechanism.

Declare the extension point explicitly so schema readers and generated
documentation can distinguish intentional extensibility from omission. Strict
resolution remains a caller-selected closed-world override.
Location documentation inherited Catalog-specific descriptions, rendering
contexts, and a severity policy that Location never defined. It also documented
a singular REST path and a filter name that do not exist in the binding/schema.

Use Location-specific llms.txt descriptions and render scopes, remove the
unsupported severity claim, and align the visible endpoint and filter names with
their canonical definitions.
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.

2 participants