Files
arah2/.planning/phases/05-routing-route-display/05-01-SUMMARY.md
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

11 KiB
Raw Permalink Blame History


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).