Files
arah2/.planning/phases/08-alt-landmark-preprocessing-routing-backend-v2/08-CONTEXT.md
T

10 KiB
Raw Blame History

Phase 8: ALT Landmark Preprocessing (Routing Backend v2) - Context

Gathered: 2026-10-25 Status: Ready for planning

## Phase Boundary

Cross-metro routes route in-budget on the free plan: ALT (A*, Landmarks, Triangle-inequality) preprocessing over the CSR graph upgrades engine A's worst case while staying $0 — the c2 rung of the measured ladder, executed (committed by Phase 6's post-planning A1 ratification). Concretely: build-time landmark distance tables, a graph-layout v2 binary that carries them, the same R2 hosting + unchanged upload script, a corpus re-run proving optimality is unaffected, the free-plan CPU gate passing with p95 under the ceiling, and the c3 degradation class retiring from KNOWN-LIMITATIONS.

No new product surfaces — the user-visible outcome is unchanged routing behavior that now covers the longest cross-metro KL routes inside the CPU budget.

## Implementation Decisions

Binary layout compatibility (user decision D-01)

  • D-01: ARG2-only, fail-honest. The graph layout version bumps; the landmark-carrying layout is a new version, and old ARG1 binaries decode to null (the existing fail-honest law in graph.ts) → the Phase 6 designed router-pending state covers them. There is exactly one decode path and one search path — no v1 fallback branch, no silent quality regression; a fork (or our own R2 during rollout) routes nothing until the v2 binary is uploaded once. Chosen over dual-version decode to keep the single-authority law: the permanent cost of two search paths (and a silently degraded mode that contradicts fail-honest) outweighs a small, honest, temporary unavailability window. The upload script stays unchanged (roadmap lock) — re-uploading the rebuilt binary is the whole migration.

Landmark budget (user decision D-02)

  • D-02: A scaling rule, not a fixed count. The selection approach is locked — build-time Dijkstra passes over well-spread anchor nodes (farthest-point/hill-climb family over the road graph; anchors are road-network nodes, NOT POIs — restaurants/shops live in the Phase 4 gazetteer). The landmark count is a measurement-justified, node-count-proportional quantity: the KL build ships whatever count the CPU-gate measurement justifies (expected in the lean ~16–32 range; the curve flattens fast at metro scale). Rationale locked in discussion: per-landmark bytes scale with node count (~1.2 MB/landmark at KL's 311k nodes), so a fixed "generous" count explodes at future Malaysia scale (~12–24 MB/landmark at country network size) while ALT's advantage itself narrows at that scale (that tier is Contraction Hierarchies' job, still deferred per Phase 6 D-02). A future Malaysia-wide build applies the same scaling rule at its own scale — nothing is over-provisioned now for a graph that doesn't exist.

Optimality verification definition (user decision D-03)

  • D-03: Equal seconds, ties allowed. The corpus re-run gate asserts: total route seconds match the pre-ALT baseline exactly (or within a named tiny epsilon, ~±0.1%) and every pair stays in the same verdict class (reachable/unreachable). ALT changes A*'s steering, never its cost model — optimal seconds are guaranteed; but bidirectional search + different potentials may legitimately change tie-breaking (a different equal-cost path). Tie-path differences are documented behavior, NOT failures. Bit-identical node paths is explicitly NOT the gate (brittle — fails on legitimate ties).

Measurement sample (user decision D-04)

  • D-04: Extend the sample for the ALT verdict. The existing 25-route seeded gate (measure-route-cpu.mjs, seed 0x06d4) stays as the CI-size snapshot; the ALT verdict additionally runs a maintainer-time extended seeded sample (~100 cross-metro worst-case pairs) for confidence that the longest routes route in-budget. Maintainer-time measurement is free — bigger evidence at zero runtime cost. Same print-the-measurement law.

Limitations retirement (user decision D-05)

  • D-05: Retire the c3 class with evidence. docs/KNOWN-LIMITATIONS.md's cross-metro CPU degradation section is replaced by a short retirement record: what changed (ALT preprocessing), the measured numbers that retired it (p95 under the ceiling, from D-04's run), and the date. The ALT row in the limitations' roadmap-pointer table flips from "Next-milestone item, not built" to built (Phase 8). The honest audit trail stays in-document (measurements print; limitations never overpromise). Note: because of D-01 there is NO "fallback for landmark-less graphs" to document — an old graph is simply router-pending, which is already a designed, documented degradation.

OpenCode's Discretion

  • Landmark-selection algorithm specifics (farthest-point vs hill-climb vs hybrid), anchor count formula constants, and any quantization of stored distances (e.g., u16-seconds research) within the scaling rule
  • Preprocessing build-time cost budget (each landmark = one full graph pass; build script runtime stays sanity-bounded)
  • The exact epsilon value and its name (named constant, replay-tested, per phase-6 constant law)
  • ARG2 layout byte design (header extensions aligned to the typed-array law; corrupt-binary fail-honest validation carried forward)
  • How the committed mini-graph fixture and test suite version-bump (new-ARG2 fixtures; old-v1 fixture kept only as a decode-rejection case)
  • KNOWN-LIMITATIONS retirement wording within the honest-engineering tone law
## Specific Ideas
  • The user explicitly asked for (and received) the landmark concept explanation: ALT anchors are abstract road-graph reference points, not places. This distinction should survive into research/planning docs so future readers don't confuse it with POI search.
  • The whole-malaysia question shaped D-02: the decision is deliberately future-compatible — a Malaysia build should re-derive landmark count from the same rule (and may reasonably graduate to CH per Phase 6's deferral), never inherit a KL-tuned constant.
  • "Fail-honest beats silent degradation" was the deciding principle for D-01 — consistent with the repo's designed-degradation philosophy (router-pending is a feature, not an error).

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Phase scope + decisions

  • .planning/ROADMAP.md §Phase 8 — goal, 3 success criteria, "graph-layout v2 with landmark distances precomputed at build time; upload script unchanged"
  • .planning/phases/06-turn-by-turn-guidance-live-driving/06-CONTEXT.md — engine A pivot (D-01/D-02), the A1 ratification committing c2 (ALT) as this phase and rejecting Workers Paid; Phase 6's CH/turn-restrictions deferral list
  • .planning/phases/06-turn-by-turn-guidance-live-driving/06-04-SUMMARY.md — the measured ladder history (full graph unidirectional 38.4 ms p95 → drop-classes rejected on correctness → bidirectional BH kept) and the real-graph build facts (311,325 nodes / 677,068 directed edges / 14.7 MB / all landmarks in one strongly-connected component)
  • .planning/phases/07-hardening-private-launch/07-CONTEXT.md — KNOWN-LIMITATIONS doc shape (07 D-08) and the consolidated budget record (07 D-06) that SC's numbers join

Codebase

  • routing/src/graph.ts — the pinned CSR binary layout ("ARG1" v1, version field, fail-honest decode): the codec this phase versions to ARG2; encode remains the single authority
  • routing/src/astar.ts — the bidirectional A* (BH) ALT builds on; its traversal law, equilibrate rule, and pure-module law
  • routing/scripts/measure-route-cpu.mjs — the CPU gate (P95_BUDGET_MS 8, 25-route seeded sample, ladder history in its header): the gate this phase must pass
  • routing/scripts/build-road-graph.mjs — the build tool where the preprocessing stage joins ( Overpass extract → dedup → encode )
  • routing/scripts/route-corpus.mjs — the corpus re-run tool the optimality gate (D-03) rides
  • routing/scripts/upload-road-graph.mjs — the unchanged fork migration path (D-01)
  • docs/KNOWN-LIMITATIONS.md — the c3 section + ALT roadmap-pointer row that D-05 retires
  • bot/src/routing/worker-router.ts — the R2-hosted graph consumer (key kl-roadgraph.v1.bin) that serves /api/route; consumes the v2 layout unchanged at its API

</canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • graph.ts encode/decode single authority — the ARG2 layout extends one module, pins one header, keeps zero-copy typed-array laws
  • astar.ts bidirectional search — ALT potentials slot into the existing lazy-potential mechanism; no new search skeleton
  • measure-route-cpu.mjs + route-corpus.mjs — the gate and corpus harness both re-run as-is (sample extension is a parameter, not a rewrite)
  • Committed mini-graph fixture — test corpus basis; version-bumped fixtures derive through the same encode path
  • snap.ts — unchanged; snapping is orthogonal to ALT (D-05 phase-6 single-authority law holds)

Established Patterns

  • Fail-honest decode → router-pending (designed degradation, never an error)
  • Named constants, replay-tested no magic numbers (applies to epsilon + landmark scaling constants)
  • Budget gates print their measurement and gate on p95 with margin
  • TDD RED→GREEN with hand-rolled runners; pure modules (no I/O in routing/src)

Integration Points

  • build-road-graph.mjs gains the preprocessing stage (one Dijkstra pass per landmark at build time)
  • R2 ROAD_GRAPH binding + upload-road-graph.mjs — same key path, new-version binary
  • Engine-B (Phase 11) downloads this same asset — the scaling rule + lean KL count keep the future phone payload in the ~30–50 MB class
  • docs/KNOWN-LIMITATIONS.md and docs/CONFIGURATION.md — the record surfaces D-05 touches

</code_context>

## Deferred Ideas
  • Contraction Hierarchies — remains the Phase 6 deferral; the noted future tier if routing ever graduates to Malaysia-wide scale (D-02's rationale points there)
  • Engine B client-side router consuming the ARG2 asset — Phase 11 (the phone-download payload concern in this phase's discussion feeds its budget documentation)
  • Turn restrictions in the graph — Phase 6 deferral stands
  • Any traffic/time-dependent weighting — v2 per REQUIREMENTS

Phase: 08-alt-landmark-preprocessing-routing-backend-v2 Context gathered: 2026-10-25