157 lines
13 KiB
Markdown
157 lines
13 KiB
Markdown
---
|
||
phase: 05-routing-route-display
|
||
plan: 02
|
||
subsystem: routing
|
||
tags: [routing, fixtures, osrm-demo, polyline6, corpus-gate, offline-gate, golden-pairs, tdd, odbl]
|
||
|
||
# Dependency graph
|
||
requires:
|
||
- phase: 05-routing-route-display/01
|
||
provides: the routing workspace — polyline6 codec (decode/encode round-trip law), haversineM, createFixtureRoutingSource/getPairOrigin/getPairs, the committed empty route-fixtures.json seed schema, the 200 m origin-tolerance matching law
|
||
- phase: 04-destination-search-kl-gazetteer
|
||
provides: the KL gazetteer (search/src/data/kl-gazetteer.json) — the destinationId namespace and the public landmark coordinates the D-12 proof keys off
|
||
provides:
|
||
- "committed 15-pair fixture dataset (routing/src/data/route-fixtures.json) — real OSRM-demo geometry, 1–3 verbatim polyline6 routes per pair, 8 pairs with ≥ 2 alternates"
|
||
- "routing/test/fixtures/golden-pairs.json — the hand-authored pinned corpus (pairIds = the ?fixpair= knob namespace for 05-05; klBbox, 100 KB gz budget, probe id, alternatives floor)"
|
||
- "routing/scripts/fetch-route-fixtures.mjs — the maintainer build-time tool (overpass-topup.mjs pattern): D-12 boundary header, pre-flight public-coordinate proof, mandatory UA, 1200 ms delay, collect-then-write, --only=<pairId> repair merge"
|
||
- "routing/scripts/route-corpus.mjs — measurePairs() measurement core + CLI reporter; the RT1–RT10 offline tolerance gate"
|
||
- "routing/test/corpus.test.mjs — the in-suite Nyquist gate asserting the same measurements (npm run routing:test)"
|
||
- "root npm delegate routing:fixtures; routing workspace fixtures/corpus scripts"
|
||
affects: [05-04 web route surface, 05-05 gates (?fixpair= + BA9 budget), phase-6 guidance]
|
||
|
||
# Tech tracking
|
||
tech-stack:
|
||
added: [] # zero new dependencies — node:zlib/node:fs built-ins + the existing workspace codec
|
||
patterns:
|
||
- "build-time fixture precompute: committed snapshot makes a free best-effort demo server safe (a policy change breaks the regeneration script, never the product)"
|
||
- "mechanical privacy proof: RT2b joins the committed dataset to the gazetteer within 1e-4 deg — 'public data between public places' enforced by test, not by promise"
|
||
- "one measurement, two reporters: exported measurePairs consumed by both the CLI and the in-suite gate (gazetteer-corpus.mjs precedent)"
|
||
- "collect-then-write regeneration: any per-pair hard failure leaves the committed file untouched; --only repairs one pair without churning fourteen"
|
||
|
||
key-files:
|
||
created:
|
||
- routing/test/fixtures/golden-pairs.json
|
||
- routing/scripts/fetch-route-fixtures.mjs
|
||
- routing/scripts/route-corpus.mjs
|
||
- routing/test/corpus.test.mjs
|
||
modified:
|
||
- routing/src/data/route-fixtures.json (empty seed → 15 real OSRM-demo pairs)
|
||
- package.json (routing:fixtures delegate)
|
||
- routing/package.json (fixtures + corpus scripts)
|
||
- routing/test/loader-env.test.mjs (L3 armed: serves pair 1 / honest null at 300 m)
|
||
- routing/test/contract.test.mjs (C6 re-pinned from empty-seed to populated-corpus state)
|
||
|
||
key-decisions:
|
||
- "C6 re-arm: 05-01's contract test pinned the seed as EMPTY with the message 'plan 05-02's build-time script populates it' — the same documented handoff as L3, re-pinned to the populated state (10..20 pairs) with the ODbL header assertions unchanged"
|
||
- "measurePairs takes an OPTIONAL 4th parameter (fixturesPath, default = the committed dataset URL relative to the script) so RT9 gzips the COMMITTED file per the RT9 spec while the plan's 3-arg call shape still works for both reporters"
|
||
- "alternatives=2 is best-effort at the demo server: 7 of 15 pairs returned a single route; 8 pairs carry 2 routes — comfortably above the RT10 floor of 4, and alternatives floor data is real, not padded"
|
||
- "the fetch script validates the ENTIRE corpus pre-flight (nothing leaves the machine unless every pair is proven public) rather than validating per-pair lazily — the strongest reading of D-12"
|
||
|
||
requirements-completed: [RTNG-01, RTNG-02]
|
||
|
||
# Metrics
|
||
duration: 9min
|
||
completed: 2026-10-23
|
||
---
|
||
|
||
# Phase 5 Plan 2: Fixture Corpus — Golden Pairs + OSRM-Demo Precompute + Offline Tolerance Gate Summary
|
||
|
||
**The fixture corpus is REAL: 15 pinned KL pairs precomputed from the public OSRM demo server (23 verbatim polyline6 routes, 8 with alternates), pinned behind the RT1–RT10 offline tolerance gate — CLI and in-suite reporters green, the D-12 "public data between public places" law mechanically proven against the gazetteer, 16.6 KB gzipped against the 100 KB early-warning budget.**
|
||
|
||
## Performance
|
||
|
||
- **Duration:** 9 min
|
||
- **Started:** 2026-10-23T21:11:43Z
|
||
- **Completed:** 2026-10-23T21:20:27Z
|
||
- **Tasks:** 2/2
|
||
- **Files touched:** 9 (4 created, 5 modified)
|
||
|
||
## Accomplishments
|
||
|
||
- **The data product exists** (D-02): `routing/src/data/route-fixtures.json` carries 15 golden pairs — 5.2 km KL Tower→KL Sentral to 23.1 km Mid Valley→IOI City Mall — each with 1–3 best-first routes stored as VERBATIM polyline6 strings from the OSRM demo (`alternatives=2&overview=full&geometries=polyline6`), ODbL header fields preserved (T-05-06). The 05-01 empty seed is resolved.
|
||
- **The maintainer tool follows the repo-tool law** (D-12): `fetch-route-fixtures.mjs` documents the maintainer-machine boundary verbatim in its header, validates the whole corpus against the gazetteer BEFORE any request leaves the machine (origin ≡ landmark within 1e-4 deg, destination known), sends a mandatory descriptive User-Agent, spaces requests 1200 ms apart, retries once on network/5xx, and writes only on total success (`collect-then-write`); `--only=<pairId>` repairs one pair without churning the rest.
|
||
- **The phase's primary acceptance instrument is armed** (amended SC3): `measurePairs()` performs RT1–RT10 fully OFFLINE — corpus size, schema + RT2b public-origin proof, route shape, probe exclusion, haversine↔summary band [0.85, 1.10] + bbox+0.05 deg, speed sanity [1.0, 30] m/s, codec round-trip per route, ODbL header surfaces, gz budget (measured **16,576 B** / 102,400 B), alternatives floor (**8** multi-route pairs / floor 4). The CLI (`npm run routing:corpus`) and the suite (`npm run routing:test`) assert the SAME numbers.
|
||
- **05-01's deferred gates armed themselves exactly as designed**: loader-env L3 now proves the default source serves pair 1 at its pinned origin and resolves honest null ~300 m away; F8's positive half runs its assertions now that pairs exist; C6 re-pinned from the empty-seed state to the populated corpus.
|
||
- **Suite state:** 35 passed, 0 failed (C1–C6, F1–F8, L1–L3, P1–P8, RT1–RT10); `npm run routing:typecheck` exit 0; zero `fetch(` occurrences in the gate (only the maintainer tool touches the network).
|
||
|
||
## Task Commits
|
||
|
||
Each task followed RED→GREEN with atomic commits:
|
||
|
||
1. **task 1: pinned golden-pairs corpus + fetch script + populated committed dataset**
|
||
- `01b0ed6` test(05-02): tighten loader-env L3 to require pair 1 serving at pinned origin (RED)
|
||
- `4e89cff` feat(05-02): pinned golden-pair corpus + OSRM-demo fetch tool + populated committed dataset (GREEN)
|
||
2. **task 2: the offline corpus tolerance gate (CLI + in-suite Nyquist gate)**
|
||
- `63ef408` test(05-02): add failing offline corpus tolerance gate suite RT1-RT10 (RED)
|
||
- `6c0880b` feat(05-02): offline corpus tolerance gate — measurePairs core + CLI + in-suite RT1-RT10 (GREEN)
|
||
|
||
## Files Created/Modified
|
||
|
||
- `routing/test/fixtures/golden-pairs.json` — the 15-pair pinned corpus verbatim from the plan (pairId, originId, pinned origin + label, destinationId), plus klBbox, fixtureGzipBudgetBytes (102400), pendingProbeDestinationId (`national-museum`), alternativesFloorPairs (4)
|
||
- `routing/scripts/fetch-route-fixtures.mjs` — the build-time tool: pre-flight D-12 proof, UA `araharah-fixtures/1.0`, 1200 ms delay, one retry, hard-fail diagnostics naming the pair, 2-space JSON + trailing newline, header fields preserved from the committed file
|
||
- `routing/src/data/route-fixtures.json` — 15 pairs / 23 routes committed (was `pairs: []`)
|
||
- `routing/scripts/route-corpus.mjs` — `measurePairs(corpus, fixtures, gazetteer, fixturesPath?)` + CLI with per-check table, measured gz size, fixed escalation ladder (`--only` repair → regenerate → re-gate); imports decode/encode/haversine from `../src/index.ts`; direct-invocation guard
|
||
- `routing/test/corpus.test.mjs` — one `test()` per RT check asserting `violations.length === 0` with named messages; prints gz size + multi-route count so suite output and CLI output agree
|
||
- `routing/test/loader-env.test.mjs` — L3 armed (RED commit); header comment updated
|
||
- `routing/test/contract.test.mjs` — C6 re-pinned to populated corpus (10..20 pairs), ODbL assertions unchanged
|
||
- `package.json` — `"routing:fixtures"` delegate
|
||
- `routing/package.json` — `"fixtures"` + `"corpus"` scripts
|
||
|
||
## Decisions Made
|
||
|
||
- **C6 re-arm** (05-01 documented the handoff on the test itself: "the seed ships EMPTY — plan 05-02's build-time script populates it"): the empty-state pin became a populated-state pin (10..20 pairs); the ODbL header gate (T-05-06) is unchanged. Same category as the plan-ordered L3 update.
|
||
- **Optional 4th `fixturesPath` parameter on measurePairs** — RT9's spec gzips the committed file; the default (dataset URL relative to the script) keeps the plan's 3-arg signature valid at both call sites.
|
||
- **Whole-corpus pre-flight validation in the fetch script** — nothing is sent until every pair is proven public, the strongest reading of D-12's red line.
|
||
- **No padding of alternates** — the demo server returned 1 route for 7 pairs; the floor is met by 8 genuine multi-route pairs. Fabricating extra routes would poison RT5/RT7.
|
||
|
||
## Deviations from Plan
|
||
|
||
### Auto-fixed Issues
|
||
|
||
**1. [Rule 1 - Stale state pin] C6 empty-seed assertion re-armed to the populated corpus**
|
||
- **Found during:** task 1 GREEN verification
|
||
- **Issue:** 05-01's `contract.test.mjs` C6 asserted `pairs.length === 0` ("the seed ships EMPTY — plan 05-02's build-time script populates it"), which the plan's task-1 acceptance ("05-01's suites pass against REAL data") supersedes; the plan explicitly ordered the analogous L3 update but C6's identical handoff pin needed the same treatment
|
||
- **Fix:** re-pinned C6 to assert 10..20 pairs; ODbL header assertions unchanged
|
||
- **Files modified:** routing/test/contract.test.mjs
|
||
- **Commit:** 4e89cff
|
||
|
||
### Judgment calls within plan laws (not deviations)
|
||
|
||
- `measurePairs`' optional 4th `fixturesPath` parameter (RT9 spec requires reading the committed file; default keeps the 3-arg shape)
|
||
- Fetch script validates the entire corpus pre-flight rather than per-pair lazily (D-12 strongest reading)
|
||
|
||
## Known Stubs
|
||
|
||
None. The 05-01 known stub (`route-fixtures.json` `pairs: []`) is **resolved** by this plan — the corpus is fully populated and gate-pinned.
|
||
|
||
## Threat Surface Scan
|
||
|
||
No new surface beyond the plan's `<threat_model>`: the only network touchpoint is the maintainer fetch tool itself (T-05-05b — the declared, documented boundary; the gate is grep-proven offline), the committed dataset ships public data only (RT2b mechanically proven), the gz budget gate gives the T-05-12 early warning (16,576 B / 102,400 B), and the ODbL surfaces are regeneration-proof (RT8). No threat flags.
|
||
|
||
## Issues Encountered
|
||
|
||
- Node lives under nvm in this environment; non-interactive shells start without it — every command sourced `~/.nvm/nvm.sh` first (05-01 precedent). No project impact.
|
||
- OSRM demo stayed reachable throughout; the plan's unreachable-server checkpoint path never triggered.
|
||
|
||
## User Setup Required
|
||
|
||
None — no external service configuration. The OSRM demo is touched only by the maintainer tool (`npm run routing:fixtures`), never by tests, CI, builds, or the runtime.
|
||
|
||
## Next Phase Readiness
|
||
|
||
- **05-04 (web route surface)**: imports `createFixtureRoutingSource` from `@araharah/routing/src/index.ts` — the committed corpus now serves 15 real pairs with best-first options, decoded geometry + cumulative distances, honest null for unknown origins (the "router pending" state).
|
||
- **05-05 (gates)**: `?fixpair=<pairId>` values are exactly the 15 committed pairIds (`getPairOrigin` resolves each); BA9's 200 KB all-in budget has 16.6 KB of fixtures against it; the C4 banned-token gate can grow with the corpus script as planned.
|
||
- **Phase 6 (guidance)**: route snapshots can embed decoded option geometry with `cumulativeDistanceM` derived at decode — the D-07 fields are live data now, not just types.
|
||
|
||
---
|
||
*Phase: 05-routing-route-display*
|
||
*Completed: 2026-10-23*
|
||
|
||
## Self-Check: PASSED
|
||
|
||
- All 4 created files verified present on disk (golden-pairs.json, fetch-route-fixtures.mjs, route-corpus.mjs, corpus.test.mjs).
|
||
- All 4 plan commits verified in git log: 01b0ed6, 4e89cff, 63ef408, 6c0880b (RED→GREEN per task).
|
||
- `npm run routing:corpus` exit 0 (RT1–RT10 ok, gz 16576 B); `npm run routing:test` 35 passed, 0 failed; `npm run routing:typecheck` exit 0.
|
||
- Committed dataset: 15 pairs, ODbL-1.0 header, zero probe-id destinations.
|
||
- STATE.md and ROADMAP.md untouched (orchestrator-owned writes); only 05-02-SUMMARY.md added (force-add, .planning/ ignored).
|