- SUMMARY: 3/3 tasks TDD RED->GREEN, 25/25 cases green, 6 commits - RoutingSource seam + polyline6 codec + fixture backend + loader proof
165 lines
11 KiB
Markdown
165 lines
11 KiB
Markdown
---
|
||
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 `dependencies` field (C5), zero banned tokens in `routing/src` with comments stripped (C4), zero imports in the codec.
|
||
|
||
## Task Commits
|
||
|
||
Each task followed RED→GREEN with atomic commits:
|
||
|
||
1. **task 1: workspace scaffold + type contracts + day-one fields**
|
||
- `69b6a20` test(05-01): scaffold routing workspace with failing contract suite (RED)
|
||
- `c92a955` feat(05-01): RoutingSource contract + day-one route model + fixture seed schema (GREEN)
|
||
2. **task 2: polyline6 codec (pure, zero-dependency)**
|
||
- `9b66f90` test(05-01): add failing polyline6 codec vectors (P1-P8) (RED)
|
||
- `4acb878` feat(05-01): polyline6 codec (pure, zero-dependency) (GREEN)
|
||
3. **task 3: geo math + fixture backend + lazy loader + public API + three-environment proof**
|
||
- `0599ffe` test(05-01): add failing fixture-source + loader-env suites (F1-F8, L1-L3) (RED)
|
||
- `3e0c5e3` feat(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; `RouteFixturesFile` dataset envelope
|
||
- `routing/src/polyline.ts` — polyline6 codec; pure (zero imports), double-math varints (no int32 wrap), throws on truncated input
|
||
- `routing/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 skip
|
||
- `routing/src/index.ts` — named-export public surface; subpath-with-extension import law documented (bare `@araharah/routing` resolves to NOTHING)
|
||
- `routing/src/data/route-fixtures.json` — committed EMPTY seed (`pairs: []`) with the ODbL `_license`/`_attribution`/`_notice` header and the `router.project-osrm.org` `_source` marker (T-05-06)
|
||
- `routing/test/{runner,harness}.mjs` — zero-dependency runner, copied verbatim from search/test
|
||
- `routing/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 gate
|
||
- `routing/test/polyline.test.mjs` — P1–P8: hand-derived + external OSRM vectors, round-trip law, malformed refusal
|
||
- `routing/test/fixture-source.test.mjs` — F1–F8: matching law, tolerance boundary, slug guard, honest null, malformed skip, lazy-index source gate, getPairOrigin
|
||
- `routing/test/loader-env.test.mjs` — L1–L3: bare Node, esbuild inline marker, default-source honest null
|
||
- `routing/package.json` + `routing/tsconfig.json` — manifest + platform-neutral tsconfig (search/ shapes)
|
||
- `package.json` (root) — `workspaces` + `routing:test`/`routing:typecheck`/`routing:corpus` delegates (corpus target lands with 05-02's script)
|
||
- `package-lock.json` — routing workspace registered
|
||
|
||
## Decisions Made
|
||
|
||
- **polyline.ts declares a structural `PolyPoint` instead of importing `RoutePoint`** — the plan's acceptance gate requires zero `from "` occurrences in the codec file, and a `import type` line 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.
|
||
- **`RouteFixturesFile` exported 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.ts` re-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.json` `pairs: []` — **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.sh` first. 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 delegate `routing:corpus` already points at `routing/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 reads `getPairOrigin`; 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; `workspaces` contains `routing`.
|
||
- STATE.md and ROADMAP.md untouched (orchestrator-owned writes).
|
||
|