- A9: island fixture re-pinned as ALL-DIRTY (every to-column holds a
B-node sentinel -> every landmark drops search-scoped -> the whole
fixture runs the haversine fallback); the old 'real ALT bounds live in
A' claim is gone — it described the per-node sentinel skip RC-1 removed
- A10: mechanism comment updated to the containment law (active list
empty at search scope, never a per-node l-mask)
- A11: structural pin rewritten to the F1 bound() shape — cleanColumnsFor
memo + activeL list must exist in code; inline '=== 0xffff' sentinel
compares BANNED (RC-1's exact shape); named-constant law pinned —
FAILS ON SHIPPED CODE (the RED gate)
- A12: re-sealed as the ALT-ACTIVE counterpart — all to-columns clean
(strongly connected ring + spur), pinned by table scan
- A13 (new): mixed clean/dirty columns via a one-way-bottleneck twin-ring
fixture — the subset-active-list branch neither A9 nor A12 exercises;
64-pair Dijkstra equivalence + honest nulls
Fails today by design: A11 (4 pins) against the shipped per-node skip.
Task 1: the D-03 gate script (measure-optimality-gate.mjs) reads the frozen
pre-ALT record, re-routes the same coordinate-pinned pairs on the current
ARG2 binary, and gates on seconds-equality (±0.1%) + verdict-class
preservation — never on node paths (equal-cost ties are documented behavior).
Rule-1 deviation the gate forced on its first run: routeAstar returned
SUBOPTIMAL real-graph routes (x038: 1234.6s vs the 1214.8s Dijkstra optimum;
x045: 1475.5s vs 1460.6s) — the fixture sweep could not see it. Root cause:
the fromTable bound d̂(ℓ,t) − d̂(ℓ,v) is one-directional Lipschitz (its u-side
term needs the REVERSE edge; a oneway does not provide one — the anchor
reached node 264611 126.8s faster than its own 1.49s upstream neighbour
264610), and the shared Ikeda average p = (h_f − h_b)/2 is consistent only
when every family inside h_f/h_b is two-sided. The toTable bound
d̂(v,ℓ) − d̂(t,ℓ) rides stored traversable edges in BOTH directions; it alone
joins the haversine now. Stop rule, reductions, meet probes, reconstruction:
untouched. landmarkTables/build verified EXACT (0 mismatches vs independent
integer Dijkstras, both rows, all 24 anchors).
- measure-optimality-gate.mjs: exported core + CLI guard (route-corpus
pattern); epsilon mirror + argVersion asserts precede any measurement;
per-pair rows, extract-drift WARNING (never a re-record), honest exit law
- astar.ts: potential() consults landmarkTo only; doc-comments carry the
one-directional-Lipschitz law and the x038/x045 evidence
- astar.test.mjs: A11 re-pinned (landmarkFrom must NOT appear in the
comment-stripped search — re-adding it trips the pin); A12 oneway-ring
sweep added (directed reach asymmetry stays Dijkstra-equal on every pair)
Battery: routing 80/80, bot 126/126, both typechecks green.
D-03 gate: GATE PASS — 53/53 routed pairs equal within ±0.1% (max |Δ| 0.005%),
verdict classes preserved (6 unreachable + 41 unsnappable, 0 re-snapped).
- The stage between edge compaction and encode feeds the graph's edges to
landmark.ts (farthestPointAnchors + landmarkTables + landmarkCoverage) —
the script quantizes and implements nothing itself (single-authority law)
- D-02 scaling rule L = clamp(round(N/13000), 16, 32) (~24 at KL's 311k
nodes) with a --landmarks <n> measured knob (positive int, hard cap 128,
parsed before --check)
- Honest gates before any write: quantum-ceiling throw -> fail with the
raise-quantum hint (Pitfall 5); per-anchor coverage >= 0.9 (the SAME
fraction for any L — the override cannot lower it, T-08-11); anchor
shortfall fails loud instead of encoding a weaker ALT
- encodeGraph receives landmarks: { quantumS, nodes, to, from } — the one
ARG2 authority; no local fallback encode
- Stats gate prints L/quantum/coverage min-mean/table bytes/sentinel totals
BEFORE the size gates; --check mirrors the facts from the decoded binary
(landmarkCount/quantum/anchors/coverage/sentinels, no network)
- A1 ladder history gains rung (c2): EXECUTED in this preprocessing stage
- RO1 upload-format-agnostic: upload-road-graph.mjs keeps KEY
'kl-roadgraph.v1.bin' + BUCKET 'araharah-graphs'; comment-stripped code
contains ZERO format tokens (landmark/encodeGraph/decodeGraph/ARG1/ARG2
magics/GRAPH_VERSION) — the migration is byte-shipping of the same key,
forever (A8 grep-shape)
- RO2 one decode path: worker-router.ts keeps GRAPH_KEY unchanged, exactly
ONE decodeGraph call in the loadGraph flow, no version-conditional branch
(GRAPH_VERSION / magics / '=== 2' absent) — an undecodable binary flows
to the existing null -> router-pending path (D-01 at the consumer)
- full battery at this commit — the FIRST where routeAstar consults real
landmark tables on the bot suite's ARG2 corridor graphs: routing 79/79
(A1 seal + A9/A10/A11 + RO1/RO2), bot 126/126 (RT trunks + cron),
routing+bot typechecks clean; worker-router.ts and upload-road-graph.mjs
untouched (git diff --stat empty)
- routeAstar's ONLY change: the h_f/h_b bodies become max(ALT triangle
bounds, haversine) — per-search Float64Array(L) constant tables in quanta
(0xFFFF written as NaN = search-scoped landmark drop), lazy per-node
max over the four triangle families with RAW u16 sentinel compares
BEFORE any float conversion, haversine max-fallback on sentinel-flooded
nodes (Pattern 3 containment)
- the (h_f - h_b)/2 combination, reductions, balanced alternation, meet
probes, BH stop rule, and real-seconds reconstruction are byte-identical
(research Pattern 1 — the survey's (pf-pr)/2 is what p_f already is)
- no Map/Object allocations (A8 green), no new imports, one search path
(L >= 1 is the decodeGraph guarantee, D-01)
- A1's 625-pair Dijkstra-equivalence sweep re-pinned under ALT: the
standing optimality proof; A9 island + A10 skip laws sealed; A11
structural pin green
- module header + routeAstar doc gain the ALT paragraph (c-metric
exactness, sentinel laws, Ikeda history intact)
- A9: two-component synthetic (3x3 grid + 4-node path), anchors HAND-PINNED
inside component A only via landmarkTables; full 169-pair sweep asserts
seconds-only Dijkstra equivalence (A-internal with ALT live, B-internal
on the haversine fallback, 72 cross pairs -> null)
- A10: origin AND goal inside the anchorless component — all families drop
at SEARCH scope, the 16 B-internal pairs still route Dijkstra-equal
- A11: comment-stripped astar.ts must reference landmarkTo/landmarkFrom/
landmarkQuantumS in CODE; one sentinel spelling (0xffff, never 65535);
A8's no-Map law unchanged — FAILS now (RED: the potential is still
pure haversine)
- case-index doc-comment extended A1-A8 -> A1-A11; buildIslandFixture
helper routes fixture tables through the ONE landmark module
graph.ts versioned IN PLACE (no second codec): GRAPH_MAGIC 0x41524732,
GRAPH_VERSION 2, HEADER_SIZE 72 (72%8==0 keeps f64 views aligned);
header gains landmarkCount u32 @60 (the old pad), landmarkQuantumS f32
@64, reserved u32 @68; the landmark suffix (u16 toTable, u16 fromTable,
u32 landmarkNodes) extends layoutFor's total arithmetic — exact-size
law covers the variable suffix BEFORE any view allocation (T-08-01).
D-01 fail-honest: ARG1 bytes / version!=2 / landmarkCount=0 / hostile
quantum / size mismatch all decode null (committed mini-graph-arg1.bin
is the standing rejection vector). GraphInput.landmarks REQUIRED and
loud-validated (T-08-04). RoadGraph gains the five landmark fields
(Plan 08-03's ALT potentials consume them).
Fixture regenerated as ARG2 through the SAME encode authority: the
builder gains the tiny real landmark stage (L=2, anchors [0,18],
quantum 0.1s, coverage 1.0 — identical bytes every re-run).
Rule-3 call-site updates forced by the required-landmarks law (missed
by the plan's files list): astar.test A3 + snap.test S3 synthetics
carry inert hand landmark blocks. Fixture regen pulled forward from
task 3 so this commit's own battery is green (74 passed, 0 failed).
farthestPointAnchors + landmarkTables + landmarkCoverage over the
ĉ metric: edge weights floor-quantized once (Math.floor(seconds/q)),
exact integer Dijkstras (Int32 labels, binary heap, lazy deletion),
0xFFFF sentinel, 65000-quantum loud overflow. Traversal law mirrors
astar.ts verbatim: bit0 edges never relaxed in EITHER direction.
Test-vector corrections caught at GREEN (expectations were wrong,
implementation was right):
- L4: rows index by anchor POSITION (row-major [l*N+v]), not node id
- L6: the c-metric farthest node from corner 0 is interior node 18
(297q: interior hops cost 2x perimeter), not corner 24 (264q) —
verified against an independent brute-force Dijkstra
A 2-node graph has no degree>=2 candidates, so farthestPointAnchors
returns empty before any Dijkstra runs and never reaches the overflow
throw. Node 1 on a 3-node path is a candidate, so the selection pass
actually exercises the law.
Hand-computed vectors for the pure landmark compute core: deterministic
degree-filtered farthest-point anchors, exact integer quanta
(floor(2.55/0.1)=25, two edges=50), 0xFFFF sentinel + self-cell laws,
oneway asymmetry, the 65000-quantum overflow throw, and the 5x5-grid
corner-to-corner 264-quanta vector with coverage 1.0.
- routing/scripts/build-glyphs.mjs: maintainer shell (Usage/Exit codes/
collect-then-write — a missing/invalid source exits 1 with the kind named
and nothing written); recolors currentColor → #EB8008 in-memory and
injects a full-bleed #1A1A1A plate (stroke="none" — it must not inherit
the root's orange stroke); the committed SVGs are THE single source and
stay untouched
- sharp 0.35.5 as a routing devDependency (maintainer-only, zero runtime
deps); 'glyphs' script added
- 8× 256×256 PNGs committed (web/public/glyphs/) — the chat turn-card's
sendPhoto URL rides the Worker's static handler; two render bugs fixed
en route: density multiplied the explicit size attrs into 2048² (dropped),
and the plate inherited the root stroke (orange frame — stroke="none")
- SVG sources verified unchanged after the run
- upload-road-graph.mjs: wrangler-shelled maintainer tool (pre-flight 1-80MB,
round-trip verify hint); bucket araharah-graphs NOT created — R2 is not yet
enabled on the account (wrangler code 10042) — dashboard one-time activation
is the phase's user_setup item
- measure-route-cpu.mjs: 25-pair gate (corpus + seeded quadrant spread),
hrtime around routeAstar, warm-up excluded, unreachable probes reported
SEPARATELY from the routed p95, exits 1 over the 8ms budget
- Rule 1 (measured): the class set lacked *_link ramps — the graph FRAGMENTED
(7.7% of nodes in severed motorway islands; corpus pairs 'unreachable').
5 link classes added -> 311325 nodes / 677068 edges / 14.7 MB, all landmarks
in one component; enforced floor corrected 1KB -> 1MB (D-01)
- ladder (a) drop tertiary+residential: p95 17.8ms but 12/25 pairs unreachable
— REJECTED on correctness (D-03), reverted, ladder history recorded
- ladder (b) bidirectional A* (BH, Ikeda potentials, reverse-CSR WeakMap):
provably optimal (A1 625-pair sweep green), alternation balanced by heap
size (raw-top comparison starves the backward side 19501:3), reduction
signs in the standard feasible form. Measured: p95 ~38-93ms — NO GATE PASS.
The potential split halves each side's steering, cancelling the halved radius
- GATE FAIL is the honest verdict: ladder (c) — Workers Paid vs layout-v2 ALT
(still $0) vs free-plan-with-degradation — requires maintainer sign-off;
docs/ROAD-GRAPH.md carries the decision table and the fork contract
- bot/src/routing/worker-router.ts: module-scope per-env graph memo (1 R2 GET per
cold isolate, shared with the 06-07 cron arm via loadGraph), snap+A*+deriveSteps,
claim-gated 401 / 4096-cap 413 / V5 bbox 400, 200-null on every miss class,
ONE best route (no alternatives), category-only logs
- webhook.ts: /api/route arm delegating to handleRoute (405 non-POST)
- config.ts: ROAD_GRAPH: R2Bucket — first R2 binding (never a KV namespace, GE5)
- wrangler.jsonc: r2_buckets ROAD_GRAPH -> araharah-graphs with the fork law comment
- routing/src/http-source.ts: createHttpRoutingSource — slug-guard-first, fail-honest
(throw/non-200/malformed/null-body -> null), defensive RouteResult validation;
exported from index.ts; ladder note: backend #2 LIVE
- A7 re-pinned (06-01 named 06-04 as the owner of createHttpRoutingSource)
- test-side fix during GREEN: the R2 fake hands decodeGraph an exact ArrayBuffer
(Node Buffers are views; encodeGraph output is already an ArrayBuffer)
- bot 103 passed / routing 65 passed; both typechecks clean
- snapToGraph: bbox+margin guard first (honest null = router-pending,
D-03), then 3x3 coarse-grid neighborhood (0.002 deg cells, WeakMap
memo per RoadGraph — LAW 1 lazy memoize, O(local) not O(n))
- projection in cos(lat)-scaled degree space (Pitfall 8); returned
distance is haversineM; result carries the best segment's nameId
(null for the unnamed sentinel) for plan 07's cron street-hop
- oneway-reverse edges stay snap-visible (direction is A*'s law);
gridBuildCount() instrumentation kept internal to the workspace
- Pinned-layout gates: round-trip, bad magic/version/truncated -> null,
zero-copy view mutation, name pool, committed mini-graph fixture pin
- Each case lazy-imports ../src/graph.ts — ERR_MODULE_NOT_FOUND is the
red state (fixture-source.test.mjs pattern)
- one test per check, mirroring search/test/corpus.test.mjs: the suite imports
measurePairs from routing/scripts/route-corpus.mjs IN-PROCESS (never spawns
the CLI) — one measurement, two reporters, no drift
- RED: the measurement core does not exist yet -> 10 named ERR_MODULE_NOT_FOUND
failures; the committed dataset itself is already green material (task 1)
- fully offline by construction: zero network in the measurement path (T-05-05)
- L3 previously pinned the 05-01 empty-seed state (honest null at the pinned origin)
- it now asserts the default fixture source SERVES pair 1 (kl-tower-to-kl-sentral)
at its pinned public origin and resolves honest null ~300 m away (outside the
200 m tolerance, D-03)
- RED until plan 05-02 populates the committed fixture dataset
- synthetic injected datasets only — never the network, never the committed file
- F3 pins the 200 m haversine tolerance boundary; F6 the defensive pair skip
- F7 the lazy-index source gate (LAW 1); L2 the esbuild inline marker
- standard encoded-polyline algorithm at 1e6 precision, double math
(no int32 shifts) so no coordinate range wraps silently
- decode THROWS on truncated varints — never partial/NaN points (T-05-02)
- round-trip law encode(decode(s)) === s proven on all vectors incl.
the external live-OSRM geometry (RESEARCH Finding 1)
- hand-derived vectors from the plan's <polyline_vectors> block
- external live-OSRM demo geometry (RESEARCH Finding 1) as ground truth
- P7 pins the truncated-varint refusal law (T-05-02)