Files
arah2/.planning/phases/05-routing-route-display/05-01-SUMMARY.md
T
araharah-executor 2ee5e4e38b docs(05-01): complete routing workspace plan
- SUMMARY: 3/3 tasks TDD RED->GREEN, 25/25 cases green, 6 commits
- RoutingSource seam + polyline6 codec + fixture backend + loader proof
2026-10-24 05:04:26 +08:00

165 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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).