Skip to content

feat(gateway): address geocoding (Nominatim) for kernels + orders - #174

Open
LamaSu wants to merge 4 commits into
masterfrom
fix/lane-geocode
Open

feat(gateway): address geocoding (Nominatim) for kernels + orders#174
LamaSu wants to merge 4 commits into
masterfrom
fix/lane-geocode

Conversation

@LamaSu

@LamaSu LamaSu commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Follow-up build lane from the pizza-order roleplay (Spark, build-only). CI verifies build+tests. Self-flagged gaps below:

Address geocoding — implementation + verification notes

Branch: fix/lane-geocode. Scope: gateway address→{lat,lng} geocoding.
Build + test only — no deploy, no push to prod, no GHCR/Railway/:prod changes.

Status: done, verified by scoped tests (mocked geocoder, no live Nominatim)

A user can now enter a street address and have it resolved to {lat,lng} at
the two seams that need real coordinates. Previously kernels/orders carried
{lat:0,lng:0} (or a hardcoded SF default), so delivery-distance and
location-aware matching couldn't work.

What was built

  1. Clean-room geocode clientpackages/gateway/src/services/geocode.ts.

    • geocode(address) -> {lat,lng} | null over the open OpenStreetMap
      Nominatim search endpoint (/search?format=json&q=...). No third-party SDK,
      no API key.
    • Descriptive User-Agent (Nominatim policy requires one), per-request
      timeout via AbortController, in-memory success cache (negative results are
      not cached, so a transient outage can't poison an address).
    • Fails soft: any network error / timeout / non-200 / empty / malformed
      body returns null. Every caller keeps its existing fallback.
    • Mockable seam: setGeocodeFetch() injects a fake fetch; IS_TEST guard
      means tests never make a live call unless a fetch is injected.
    • Optional env overrides (all have defaults, none are secrets):
      PCC_GEOCODE_URL, PCC_GEOCODE_USER_AGENT, PCC_GEOCODE_TIMEOUT_MS.
  2. Seam 1 — kernel registration (facades/kernel.facade.ts, register()):
    when an operator supplies physicalAddress (or the legacy string-form

LamaSu and others added 4 commits June 17, 2026 18:57
Address -> {lat,lng} via the open OpenStreetMap Nominatim search endpoint.
No third-party SDK and no API key: a small client with a descriptive
User-Agent, a per-request timeout (AbortController), and an in-memory cache.
Fails soft (null on any error/timeout/empty) so callers keep their fallback.
Injectable fetch seam (setGeocodeFetch) + an IS_TEST guard so tests never
make live Nominatim calls. 12 unit tests cover happy path, caching, and every
soft-failure mode.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
KernelFacade.register() now resolves a postal address to coordinates when an
operator supplies physicalAddress (or the legacy string-form location) but no
explicit { lat, lng }. Previously every such kernel persisted location {0,0},
which silently broke delivery-distance and location-aware matching.

Soft-fail: on a geocode miss the {0,0} sentinel is kept (prior behaviour). When
explicit coords are sent the geocoder is never consulted. 4 wiring tests cover
address->coords, legacy string form, explicit-coords short-circuit, and miss.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
POST /api/demo/pizza-order now resolves the buyer's deliveryAddress to
coordinates when no deliveryLocation is supplied, instead of always falling back
to the hardcoded SF drop point. The resolved point feeds the compose location
filter, so shop/driver matching is anchored to the real delivery address.

Order of precedence: explicit deliveryLocation > geocoded address > SF default.
2 wiring tests cover address->coords (order planned at the geocoded point) and
the explicit-coords short-circuit (geocoder never called).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant