From 2ee5e4e38b29b3b88b53f1f80d8dbf95dcc77470 Mon Sep 17 00:00:00 2001 From: araharah-executor Date: Sat, 24 Oct 2026 05:04:26 +0800 Subject: [PATCH] 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 --- .../05-routing-route-display/05-01-SUMMARY.md | 164 ++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 .planning/phases/05-routing-route-display/05-01-SUMMARY.md diff --git a/.planning/phases/05-routing-route-display/05-01-SUMMARY.md b/.planning/phases/05-routing-route-display/05-01-SUMMARY.md new file mode 100644 index 0000000..d4607da --- /dev/null +++ b/.planning/phases/05-routing-route-display/05-01-SUMMARY.md @@ -0,0 +1,164 @@ +--- +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 ``: 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). +