- 3 tasks: build tool + real 14.7MB KL graph; TDD /api/route + backend #2 + RP re-pins; upload/measure/fork-docs + the A1 ladder to its decision point - gates: routing 65/65, bot 103/103, both typechecks clean - escalations inside: A1 CPU gate FAIL (ladder a rejected on correctness, b sound-but-net-zero, c needs maintainer sign-off); R2 bucket awaits one-time dashboard activation (user_setup) - STATE.md / ROADMAP.md untouched (orchestrator-owned)
19 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 06-turn-by-turn-guidance-live-driving | 04 | routing |
|
|
|
|
|
|
|
|
|
47min | 2026-10-24 |
Phase 6 Plan 4: Engine-A Server Surface — Road Graph, R2, /api/route, Backend #2 Summary
Engine A's server surface is REAL: a 14.7 MB KL road graph built reproducibly from Overpass (311k nodes, all landmarks one component), a Bearer-gated /api/route that routes arbitrary current positions with named turn steps behind an isolate-memoized R2 load, the client's createHttpRoutingSource backend #2, RP1/RP2 re-pinned — and the phase's top risk (A1) measured honestly: the free-plan CPU gate FAILS and is escalated with the full ladder evidence.
Performance
- Duration: ~47 min
- Started: 2026-10-24T05:15:50Z
- Completed: 2026-10-24T06:03:40Z
- Tasks: 3/3 (task 2 TDD RED→GREEN)
- Files: 14 (9 created, 5 modified)
Accomplishments
- The D-01 build tool exists and the real graph is built (task 1):
build-road-graph.mjsderives the bbox mechanically from the committed corpus (gazetteer + golden pairs ± 0.05°, printed), fetches ONE Overpass extract of the drivable classes, dedups nodes by rounded coordinate, encodes throughencodeGraph(the layout's single authority), and gates: enforced >1 MB ≤80 MB, advisory 30–80 MB band (printed: 14.7 MB below band — review prompt per plan).--checkre-decodes and re-prints the verdict offline. Artifact:routing/data/kl-roadgraph.v1.bin(gitignored, fork-rebuildable), 311,325 nodes / 677,068 directed edges / 21,467 street names / max 110 km/h. - The routing oracle is live and closed (task 2):
POST /api/routederives identity from the Bearer claim (401 class, category-only logs), caps bodies at 4096 (413), type+range-validates the origin againstgraph.bboxBEFORE any graph work (400), and answers 200 null on every miss class — unknown destination, snap miss, disconnected pair, absent graph, corrupt binary — the D-03 router-pending contract crossing HTTP unchanged. The happy path returns ONEengine-aoption withderiveSteps' named/kind-bearing steps and the day-onecumulativeDistanceM. Bot suite: RT1–RT11 + H1–H8, RED→GREEN. - The client seam has backend #2 (task 2):
createHttpRoutingSource({endpoint, bearer, fetchImpl})— slug-guard first, Authorization only when the injected getter holds a token, fail-honest on throw/non-200/malformed/null-body, defensive RouteResult validation (destination match, options 1..3, finite numbers, cumulative 1:1 with points). Exported fromrouting/src/index.ts; A7 re-pinned (06-01's comment named this plan as the owner). - The negative-space gates were re-pinned by the change that invalidated them (task 2): RP1 now demands claim-bound LOCAL routing (getClaimByToken in worker-router.ts, zero
fetch(, no routing hostnames, every/api/routemention wired to the claim-deriving module); RP2 allowlists@araharah/search/*+@araharah/routing/*subpath-with-extension imports only; GE5 (exactly ONE kv_namespaces) still green beside the firstr2_bucketsbinding. - The fork contract is scripted and documented (task 3):
upload-road-graph.mjs(wrangler shell + pre-flight + round-trip verify hint),measure-route-cpu.mjs(the printed gate),docs/ROAD-GRAPH.md(build→check→upload→deploy, ODbL attribution, envelope law, the CPU-gate decision table). - A1 was measured, not hoped — and the ladder was executed to its decision point (task 3, see Deviations + escalation below).
Task Commits
- task 1: road-graph build tool + real KL graph
d51fd8bfeat(06-04): road-graph build tool — Overpass extract → pinned CSR binary with the D-01 stats gate
- task 2: http-source + worker-router + /api/route + RP re-pins (TDD)
0abe64etest(06-04): failing http-source suite H1-H8 + /api/route block RT1-RT11 + RP1/RP2 re-pin (RED)020eb92feat(06-04): /api/route engine-A arm + http-source backend #2 — Bearer-gated, honest-null, RP re-pinned (GREEN)
- task 3: R2 upload + CPU measurement gate + the A1 ladder executed
ea9584dfeat(06-04): R2 upload + A*-CPU measurement gate + the A1 ladder executed to its decision point
Files Created/Modified
See frontmatter key-files. The graph binary itself is a gitignored artifact by design (D-01: forks build their own).
Decisions Made
See key-decisions. The three with teeth: *_link classes are load-bearing (fragmentation measured); the A1 gate is honestly failing and escalated with evidence; the graph memo is the per-env WeakMap form of the plan's graphP ??= law.
Deviations from Plan
Auto-fixed Issues
1. [Rule 1 - Bug] The graph fragmented: *_link road classes were missing from the drivable set
- Found during: task 3 (the CPU measurement exposed it — corpus pairs "unreachable", 7.7% of nodes in severed motorway islands)
- Fix: added motorway/trunk/primary/secondary/tertiary
_linkclasses (Overpass regex + way filter + speed table); rebuilt: every landmark snaps into one 305k-node strongly-connected component. Also corrected the enforced floor from 1 KB to the plan's 1 MB. - Files modified: routing/scripts/build-road-graph.mjs
- Commit:
ea9584d
2. [Rule 1 - Bug] The first bidirectional A had two soundness bugs, both caught by the 06-01 gates*
- Found during: task 3 ladder (b) GREEN — A1 (625-pair Dijkstra equivalence) and A2 (oneway law) went RED
- Issue: the backward branch scanned
u's OUT-edges as in-edges (phantom arcs — A1 caught a 6.66s "path" against Dijkstra's 26.67s); then the reduction signs were mirrored-infeasible on both sides (correct paths via the constant meet-sum, but the searches wandered — 19,504 settles with ONE meet probe) - Fix: per-graph reverse CSR (WeakMap, the snap-grid LAW 1 pattern) for in-edges; reductions in the standard feasible form (
c + p_f(u) − p_f(v)forward,c + p_b(u) − p_b(v)backward); alternation balanced by heap size after measuring that raw-top comparison starves the backward side 19501:3 - Files modified: routing/src/astar.ts
- Commit:
ea9584d
The A1 Escalation (plan-directed stop-and-surface — NOT silently closed)
The plan's must-have "p95 inside the 10 ms free-plan budget" is NOT MET. Measured on the real linked graph (25-pair gate, warm isolate, hrtime around routeAstar; run-to-run variance on this box ≈ 2x, best observed p95 ≈ 35-40 ms):
| Ladder step | Result |
|---|---|
| full graph, unidirectional | p95 ≈ 38 ms — FAIL |
| (a) drop tertiary/residential | p95 ≈ 18 ms — FAIL and 12/25 pairs unreachable (severed the landmark-to-arterial mesh) — rejected on D-03 correctness, reverted |
| (b) bidirectional BH (shipped) | p95 ≈ 38 ms — FAIL: the potential split halves each side's steering, cancelling the halved radius. Kept: sound, no worse, better on short routes |
| (c) DECISION REQUIRED | Workers Paid ($5/mo, breaks $0) · layout-v2 ALT landmark graph (build-time preprocessing, $0 runtime) · accept free-plan with honest degradation (long reroutes → 1102 → client's fail-honest null → router-pending) |
Short product routes (2–8 km — the common reroute) measured 2–8 ms: inside budget. The failures concentrate on 20–30 km cross-metro routes.
Test-side fixes during GREEN (executor bugs, not plan deviations)
- The R2 fake initially handed
decodeGrapha Node Buffer (a view, not an ArrayBuffer) — every graph case degraded honest-null; fixed to an exact ArrayBuffer copy. The corridor fake then needed aninstanceof ArrayBufferbranch (encodeGraph's product IS an ArrayBuffer).
Known Stubs
None. All exports are fully wired. Two honest gaps by design: the R2 bucket is not yet created (account-level activation — the phase's user_setup item, below), and long-route CPU exceeds the free-plan budget (escalated, documented, degrading honestly).
Threat Surface Scan
No new surface beyond the plan's <threat_model>: /api/route is the planned boundary (T-06-01 Bearer-claim 401 class tested; T-06-01b V5 bbox 400 before compute, unknown-dest short-circuit, ONE route — alternatives prohibited; T-06-03 corrupt/absent binary → null; T-06-04 category-only [route] rejected: <category> logs asserted verbatim in RT1–RT3). RP1's new positive-space form (no fetch( in worker-router, no routing hosts in bot/src) guards the fork surface. The upload script is maintainer-surface only (shells wrangler; never imported by runtime code). No threat flags.
Issues Encountered
- A concurrent writer modified
routing/scripts/build-road-graph.mjsbetween my build run and my task-1 commit — commitd51fd8bcontains a coherent variant implementation I did not author (my in-session draft's output strings differ from the committed file's). I did not discover the author. Handling: treated the committed variant as the base of record, verified every subsequent edit before staging (grepbefore eachgit add), and re-verified task-2 files at HEAD are my versions (they are). No further interference was observed after 13:28 local. Flagging for the orchestrator in case another session is active on this tree. - Run-to-run CPU variance ≈ 2x on this machine (p95 37.7 ms vs 93.6 ms for identical code+input across runs) — shared-box load. The escalation conclusion is robust (best-case is still ~5x over budget).
- kl-tower→kl-sentral measures "unreachable": both landmarks sit in the giant undirected component, but the snap can land on a one-way sink node (the D-05 law: snapping ignores direction; A* enforces it) → directed dead end → honest 200 null. Production UX consideration (retry from the segment's other endpoint) belongs to 06-05's client wiring; noted here so the real-drive gate isn't surprised.
User Setup Required (the phase's cloudflare-r2 item)
- Enable R2 on the Cloudflare account (one-time, dashboard-only): Cloudflare dashboard → R2 → Get started (requires billing verification — the free tier itself is $0). Wrangler is authenticated but the API answers
code: 10042 "Please enable R2 through the Cloudflare Dashboard". - Create the bucket:
npx wrangler r2 bucket create araharah-graphs(or any name — must matchbot/wrangler.jsonc'sROAD_GRAPH.bucket_name). - Upload:
node routing/scripts/upload-road-graph.mjs, then verify with the round-trip command it prints. Forks: the same three steps aredocs/ROAD-GRAPH.md.
Next Phase Readiness
- 06-05 (web wiring):
createHttpRoutingSourceis exported and tested — wire the ladder (fixture → engine-a) inweb/src, and the router-pending state now has a real trigger (200-null on miss + fail-honest on transport error). The one-way-sink retry idea (above) lands here. - 06-07 (cron): import
loadGraphfromworker-router.ts— the SAME per-isolate memo serves both arms (RT10 proves one R2 GET across requests). - The real-drive gate: budget expectations are now measured facts — short reroutes fit the free plan; cross-metro routes do not (see the escalation).
- The A1 decision (c1/c2/c3) blocks nothing in the remaining phase plans but must be settled before v1 exit; the verifier should treat GUID-02's engine side as delivered-with-escalation.
Phase: 06-turn-by-turn-guidance-live-driving Completed: 2026-10-24
Self-Check: PASSED
- All 10 created/modified artifacts verified on disk (3 scripts, http-source, worker-router, 2 test files, re-pinned gates, fork docs, gitignore) + the built graph binary (14,749,343 B,
--checkgreen). - All 4 plan commits verified in git log:
d51fd8b,0abe64e(RED),020eb92(GREEN),ea9584d— task 2's RED→GREEN gate sequence intact. npm run routing:test: 65 passed / 0 failed (A1's 625-pair Dijkstra-equivalence sweep green on the bidirectional A*).npm test -w bot: 103 passed / 0 failed (RT1–RT11, GE5 one-kv-namespace, re-pinned RP1/RP2).routing:typecheck+bot:typecheckclean.- STATE.md and ROADMAP.md untouched (their working-tree modifications predate this session — orchestrator-owned writes); only this SUMMARY force-added.
- Honest gaps recorded, not papered over: the A1 CPU gate FAILS (escalated with ladder evidence, above); the R2 bucket awaits account-level activation (user_setup).