- SUMMARY: 3/3 tasks TDD RED->GREEN, 25/25 cases green, 6 commits - RoutingSource seam + polyline6 codec + fixture backend + loader proof
11 KiB
phase: 05-routing-route-display plan: 01 subsystem: routing tags: [routing, fixture-backend, polyline6, encoded-polyline, routing-source, npm-workspaces, tdd, osrm]
Dependency graph
requires:
- phase: 04-destination-search-kl-gazetteer provides: the search/ workspace pattern (RoutingSource mirrors SearchSource: interface + backend ladder + lazy loader + hand-rolled runners) and the gazetteer destinationId namespace the route requests carry provides:
- "@araharah/routing — the fifth buildless npm workspace (pure .ts + .json, zero runtime deps)"
- "RoutingSource interface + full route model with D-07 day-one fields (RouteStep typed, RouteGeometry.cumulativeDistanceM)"
- "polyline6 codec (decode/encode, 1e-6 grid, round-trip law, truncated-input refusal)"
- "geo math: haversineM + deriveCumulativeDistanceM"
- "createFixtureRoutingSource — backend #1 with the 200 m origin-tolerance matching law and honest-null pending semantics"
- "committed EMPTY route-fixtures.json seed with ODbL header (schema + license surfaces ship now)"
- "getPairOrigin / getPairs introspection for the corpus runner (05-02) and ?fixpair= harness knob (05-05)"
- "three-environment loader proof (bare Node 24, esbuild, Vite-family) — L1–L3" affects: [05-02 fixtures corpus, 05-04 web route surface, 05-05 gates, phase-6 guidance]
Tech tracking
tech-stack: added: [] # zero new dependencies — buildless TS consumed via workspace paths (search/ precedent) patterns: - "RoutingSource seam with documented backend ladder (fixture → wasm-router → valhalla), backend selected in code at the import site" - "polyline6 verbatim storage + pure codec with round-trip law as the corpus gate's free assertion" - "lazy memoized index at factory-closure scope; module scope holds only the static JSON import binding" - "defensive pair validation at index build (malformed → skipped → honest null, never a throw)"
key-files: created: - routing/package.json - routing/tsconfig.json - routing/src/types.ts - routing/src/polyline.ts - routing/src/geo.ts - routing/src/fixture-source.ts - routing/src/index.ts - routing/src/data/route-fixtures.json - routing/test/runner.mjs - routing/test/harness.mjs - routing/test/contract.test.mjs - routing/test/polyline.test.mjs - routing/test/fixture-source.test.mjs - routing/test/loader-env.test.mjs modified: - package.json (workspaces + routing:test/typecheck/corpus delegates) - package-lock.json (routing workspace registered)
key-decisions:
- "polyline.ts is zero-import: it declares a structural PolyPoint instead of importing RoutePoint because the plan's acceptance gate greps polyline.ts for zero
from \"\"occurrences; structural identity keeps full type compatibility" - "F8's positive half (getPairOrigin resolving a known pair) ships as a visible SKIP while the committed seed is empty — the lookup machinery is proven by F2's injected dataset; the positive half arms automatically when 05-02 populates the corpus (loader-env L2/L3 precedent)"
- "RouteFixturesFile added to types.ts (beyond the plan's listed ten interfaces) because createFixtureRoutingSource(data?) needs a public parameter type; index.ts re-exports kept exactly per plan"
patterns-established:
- "RoutingSource ladder: interface header documents backends #2/#3 without shipping them (the SearchSource analog, D-04)"
- "Null contract as contract documentation: 'router pending' grep-pinned by C3 so the semantics cannot silently drift"
- "Three zero-laws gate (C4): comments stripped, then banned tokens (logging/storage/network) grepped across routing/src — grows with 05-02's script"
requirements-completed: [RTNG-01]
Metrics
duration: 14min completed: 2026-10-23
Phase 5 Plan 1: Routing Workspace — RoutingSource Seam + Fixture Backend + polyline6 Codec Summary
@araharah/routing — the fifth buildless workspace landing the D-01 RoutingSource seam (fixture → wasm-router → valhalla ladder), the polyline6 codec proven against live OSRM geometry, 200 m-tolerance fixture matching with honest-null pending semantics, and a committed ODbL-headed empty fixture seed — 25/25 tests green in three environments.
Performance
- Duration: 14 min
- Started: 2026-10-23T20:50:09Z
- Completed: 2026-10-23T21:04:00Z
- Tasks: 3/3
- Files modified: 16 (14 created, 2 modified)
Accomplishments
- The routing seam exists (D-01):
RoutingSource+ the full route model (RoutePoint→RouteResult) with the D-07 day-one fields typed and grep-pinned by tests (cumulativeDistanceM,steps: RouteStep[], the GUID-05/06 obligation note) — plans 05-02/05-04/05-05 and Phase 6 import these exact names. - Backend #1 works end-to-end (D-02/D-03): fixture matching law (destinationId equality + 200 m haversine origin tolerance), best-first option order,
steps: []always (T-05-07), defensive malformed-pair skip, lazy memoized index (LAW 1), static JSON import (LAW 2), zero logging/storage/network (LAW 3). - The polyline6 codec is proven against external ground truth (RESEARCH Finding 1): 8 vector cases including the live OSRM demo geometry (decode byte-exact + full-string round-trip) and the truncated-varint refusal law (T-05-02).
- Three-environment loader proof (Phase 4's top mechanical risk pattern): bare Node 24 static JSON import (L1), esbuild bundle inline marker
router.project-osrm.org(L2), default source serving honest null from the committed seed (L3). - Zero-dependency posture verified mechanically: no
dependenciesfield (C5), zero banned tokens inrouting/srcwith comments stripped (C4), zero imports in the codec.
Task Commits
Each task followed RED→GREEN with atomic commits:
- task 1: workspace scaffold + type contracts + day-one fields
69b6a20test(05-01): scaffold routing workspace with failing contract suite (RED)c92a955feat(05-01): RoutingSource contract + day-one route model + fixture seed schema (GREEN)
- task 2: polyline6 codec (pure, zero-dependency)
9b66f90test(05-01): add failing polyline6 codec vectors (P1-P8) (RED)4acb878feat(05-01): polyline6 codec (pure, zero-dependency) (GREEN)
- task 3: geo math + fixture backend + lazy loader + public API + three-environment proof
0599ffetest(05-01): add failing fixture-source + loader-env suites (F1-F8, L1-L3) (RED)3e0c5e3feat(05-01): geo math + fixture RoutingSource backend + public API (GREEN)
Files Created/Modified
routing/src/types.ts— the seam + route model; D-01 ladder and D-03 null contract in the header; D-07 day-one fields documented with the GUID-05/06 obligation verbatim;RouteFixturesFiledataset enveloperouting/src/polyline.ts— polyline6 codec; pure (zero imports), double-math varints (no int32 wrap), throws on truncated inputrouting/src/geo.ts—haversineM(R = 6 371 000 m) +deriveCumulativeDistanceM([0] === 0, non-decreasing)routing/src/fixture-source.ts— backend #1:ORIGIN_TOLERANCE_M = 200,createFixtureRoutingSource(data?),getPairOrigin,getPairs; slug guard first; lazy memoized index; defensive pair skiprouting/src/index.ts— named-export public surface; subpath-with-extension import law documented (bare@araharah/routingresolves to NOTHING)routing/src/data/route-fixtures.json— committed EMPTY seed (pairs: []) with the ODbL_license/_attribution/_noticeheader and therouter.project-osrm.org_sourcemarker (T-05-06)routing/test/{runner,harness}.mjs— zero-dependency runner, copied verbatim from search/testrouting/test/contract.test.mjs— C1–C6: seam shape, day-one field grep, null-contract doc grep, three zero-laws gate, zero-deps gate, ODbL header gaterouting/test/polyline.test.mjs— P1–P8: hand-derived + external OSRM vectors, round-trip law, malformed refusalrouting/test/fixture-source.test.mjs— F1–F8: matching law, tolerance boundary, slug guard, honest null, malformed skip, lazy-index source gate, getPairOriginrouting/test/loader-env.test.mjs— L1–L3: bare Node, esbuild inline marker, default-source honest nullrouting/package.json+routing/tsconfig.json— manifest + platform-neutral tsconfig (search/ shapes)package.json(root) —workspaces+routing:test/routing:typecheck/routing:corpusdelegates (corpus target lands with 05-02's script)package-lock.json— routing workspace registered
Decisions Made
- polyline.ts declares a structural
PolyPointinstead of importingRoutePoint— the plan's acceptance gate requires zerofrom "occurrences in the codec file, and aimport typeline would violate it; structural identity is fully type-compatible at every consumer. - F8's positive half is a visible SKIP while the committed seed is empty (its lookup target only exists after 05-02 populates the corpus). The negative/garbage halves are fully asserted now; the positive half arms automatically once pairs land. This mirrors the loader-env L2/L3 conditional-skip precedent.
RouteFixturesFileexported from types.ts (an 11th interface beyond the plan's listed ten) —createFixtureRoutingSource(data?)needs a public parameter type for tests and 05-02's corpus runner.index.tsre-exports kept exactly per plan.
Deviations from Plan
Auto-fixed Issues
None — no bugs, no blocking issues. The three items above are judgment calls within the plan's stated laws, documented for the verifier.
Known Stubs
routing/src/data/route-fixtures.jsonpairs: []— intentional per plan ("plan 05-02 populates it via the build-time script"). Until then the committed corpus serves honest nulls (F1/L3 pin this). Resolved by plan 05-02; no UI surface consumes fixtures before 05-04.
Threat Surface Scan
No new surface beyond the plan's <threat_model>: no network endpoints, no auth paths, no storage, no schema at trust boundaries. All six register entries (T-05-01/02/03/04/06/07) have implemented mitigations covered by gates C4, P7, F4, the in-code backend selection, C6, and F2 respectively. No threat flags.
Issues Encountered
- Node lives under nvm in this environment and non-interactive shells start without it — every verification command sourced
~/.nvm/nvm.shfirst. No project impact.
User Setup Required
None — no external service configuration required. (The OSRM demo server is touched only by 05-02's maintainer script, not by this plan.)
Next Phase Readiness
- 05-02 (fixtures corpus): imports
getPairs/getPairOrigin/codec round-trip for the corpus gate; root delegaterouting:corpusalready points atrouting/scripts/route-corpus.mjs(to be created); the committed seed schema is the write target. - 05-04 (web route surface): imports
createFixtureRoutingSource+ model types from@araharah/routing/src/index.ts(subpath-with-extension law documented in the module header). - 05-05 (gates):
?fixpair=knob readsgetPairOrigin; the C4 banned-token gate is designed to grow with the corpus script.
Phase: 05-routing-route-display Completed: 2026-10-23
Self-Check: PASSED
- All 14 created files verified present on disk.
- All 6 plan commits verified in git log (
69b6a20,c92a955,9b66f90,4acb878,0599ffe,3e0c5e3). npm run routing:test: 25 passed, 0 failed.npm run routing:typecheck: exit 0.- Root delegates verified from repo root;
workspacescontainsrouting. - STATE.md and ROADMAP.md untouched (orchestrator-owned writes).