docs(08.1): create phase plans for contraction hierarchies routing backend v3
This commit is contained in:
@@ -296,13 +296,15 @@ Notes:
|
||||
|
||||
### Phase 08.1: Contraction Hierarchies (Routing Backend v3) — the lever that closes SC-1: build-time node ordering + contraction + shortcut edges, simple uphill bidirectional query, optimality by construction (INSERTED)
|
||||
|
||||
**Goal:** [Urgent work - to be planned]
|
||||
**Requirements**: TBD
|
||||
**Goal:** Build-time node ordering + contraction + shortcut edges over the KL road graph, a simple uphill bidirectional query replacing the ALT-steered bidirectional A* as THE one search path, and optimality by construction — so cross-metro routes finally route inside the free-plan CPU budget and the c3 degradation class retires with measured evidence (or the record shrinks honestly if the measurement says otherwise).
|
||||
**Requirements**: ROADMAP-08.1-GOAL, D-01..D-13 (phase-scoped decisions; no v1 REQ ids added)
|
||||
**Depends on:** Phase 8
|
||||
**Plans:** 5/5 plans complete
|
||||
**Plans:** 3 plans
|
||||
|
||||
Plans:
|
||||
- [ ] TBD (run /gsd-plan-phase 08.1 to break down)
|
||||
- [ ] 08.1-01-PLAN.md — Pure CH construction core + ARG3 codec + uphill bidirectional CH query as the sole search path; ALT deleted; ARG3 fixtures and bot graph builders rebuilt (D-04/D-05/D-07/D-10/D-11/D-12)
|
||||
- [ ] 08.1-02-PLAN.md — CH production build stage + measured ARG3 build + unchanged D-13 optimality/oracle gates + local CPU verdict and conditional D-03 drift-guard re-anchor (D-01/D-02/D-03/D-08/D-09/D-13)
|
||||
- [ ] 08.1-03-PLAN.md — Public ARG3 fork contract + measured c3 retire-or-shrink record + direction-split experiment closed as superseded (D-01/D-03/D-06/D-08/D-09/D-10/D-13)
|
||||
|
||||
### Phase 9: Routing Extras (Toll / Speed / Share / Voice)
|
||||
**Goal**: The REQUIREMENTS extras go live on the engine-A stack.
|
||||
|
||||
+386
@@ -0,0 +1,386 @@
|
||||
---
|
||||
phase: 08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- routing/src/contraction.ts
|
||||
- routing/src/ch-query.ts
|
||||
- routing/src/graph.ts
|
||||
- routing/src/astar.ts
|
||||
- routing/src/index.ts
|
||||
- routing/src/landmark.ts
|
||||
- routing/test/contraction.test.mjs
|
||||
- routing/test/ch-query.test.mjs
|
||||
- routing/test/graph.test.mjs
|
||||
- routing/test/astar.test.mjs
|
||||
- routing/test/landmark.test.mjs
|
||||
- routing/test/fixtures/build-mini-graph.mjs
|
||||
- routing/test/fixtures/mini-graph.bin
|
||||
- routing/test/fixtures/mini-graph-arg2.bin
|
||||
- bot/test/webhook.test.mjs
|
||||
- bot/test/guidance-cron.test.mjs
|
||||
autonomous: true
|
||||
requirements:
|
||||
- ROADMAP-08.1-GOAL
|
||||
- D-04
|
||||
- D-05
|
||||
- D-07
|
||||
- D-10
|
||||
- D-11
|
||||
- D-12
|
||||
must_haves:
|
||||
truths:
|
||||
- "The routing package builds a deterministic contraction hierarchy with a strict total rank, valid upward shortcut edges, and an overlay-only witness search."
|
||||
- "Every route request uses one bidirectional uphill CH query; the old ALT-steered bidirectional A* internals and landmark tables are absent from runtime code and binary output."
|
||||
- "ARG3 is a little-endian typed-array layout that decodes only exact ARG3 bytes; ARG1 and ARG2 bytes decode to null and reach the existing router-pending state."
|
||||
- "The reported route seconds are summed from the unpacked original-edge path, and every route remains optimal against the Dijkstra oracle."
|
||||
- "The committed mini-graph fixture and bot test graphs are produced through the same ARG3 encode path as the production graph."
|
||||
artifacts:
|
||||
- path: "routing/src/contraction.ts"
|
||||
provides: "Pure deterministic CH construction core: ordering, contraction, overlay witness search, shortcut emission"
|
||||
- path: "routing/src/ch-query.ts"
|
||||
provides: "Pure uphill bidirectional CH query, shortcut unpacking, and original-edge cost recomputation"
|
||||
- path: "routing/src/graph.ts"
|
||||
provides: "ARG3 codec authority: rank + shortcut suffix, exact-size and alignment validation"
|
||||
- path: "routing/test/contraction.test.mjs"
|
||||
provides: "Construction invariants, hand fixture, overlay witness law, tripwire, determinism"
|
||||
- path: "routing/test/ch-query.test.mjs"
|
||||
provides: "Directed oneway, original-edge seconds, one-path, and deleted-ALT structural pins"
|
||||
- path: "routing/test/fixtures/mini-graph-arg2.bin"
|
||||
provides: "Immutable ARG2 decode-rejection vector"
|
||||
key_links:
|
||||
- from: "routing/src/contraction.ts"
|
||||
to: "routing/src/graph.ts"
|
||||
via: "ContractionResult rank + shortcut CSR encoded through GraphInput.ch"
|
||||
pattern: "contractGraph|encodeGraph"
|
||||
- from: "routing/src/ch-query.ts"
|
||||
to: "routing/src/graph.ts"
|
||||
via: "zero-copy RoadGraph chRank and shortcut CSR views"
|
||||
pattern: "chRank|shortcutStart"
|
||||
- from: "routing/src/astar.ts"
|
||||
to: "routing/src/ch-query.ts"
|
||||
via: "routeAstar delegates every request to the uphill CH query"
|
||||
pattern: "routeCH|ch-query"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Implement the routing-backend-v3 data plane and its single search path: build-time contraction with shortcut edges, ARG3 binary persistence, and one uphill bidirectional CH query used by every request.
|
||||
|
||||
Purpose: This is the mechanism that closes SC-1. The hierarchy prunes the request-time search structurally, while D-04/D-05 remove the ALT engine and its 28.5 MB landmark suffix rather than leaving a dormant fallback.
|
||||
Output: Pure CH construction/query modules, ARG3 codec, CH-backed routeAstar, ARG3 fixtures, and updated routing/bot test suites.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.config/opencode/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.config/opencode/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-CONTEXT.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-RESEARCH.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-PATTERNS.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-VALIDATION.md
|
||||
@routing/src/graph.ts
|
||||
@routing/src/astar.ts
|
||||
@routing/src/landmark.ts
|
||||
@routing/src/index.ts
|
||||
@routing/test/graph.test.mjs
|
||||
@routing/test/astar.test.mjs
|
||||
@routing/test/fixtures/build-mini-graph.mjs
|
||||
@routing/test/rollout.test.mjs
|
||||
</context>
|
||||
|
||||
<interfaces>
|
||||
Existing contracts to preserve:
|
||||
```typescript
|
||||
// routing/src/astar.ts
|
||||
export interface AstarResult {
|
||||
nodes: number[];
|
||||
seconds: number;
|
||||
distanceM: number;
|
||||
}
|
||||
export function routeAstar(graph: RoadGraph, originNode: number, goalNode: number): AstarResult | null;
|
||||
export function deriveSteps(graph: RoadGraph, nodes: number[]): RouteStep[];
|
||||
export function classifyTurn(prevBearingDeg: number, nextBearingDeg: number): GuidanceTurnKind;
|
||||
```
|
||||
|
||||
New internal contracts to implement:
|
||||
```typescript
|
||||
// routing/src/contraction.ts
|
||||
export interface ContractionInput {
|
||||
nodeCount: number;
|
||||
edges: Array<{ from: number; to: number; seconds: number; flags: number }>;
|
||||
}
|
||||
export interface ContractionResult {
|
||||
rank: Uint32Array;
|
||||
shortcutStart: Uint32Array;
|
||||
shortcutTarget: Uint32Array;
|
||||
shortcutSeconds: Float32Array;
|
||||
shortcutVia: Uint32Array;
|
||||
stats: {
|
||||
shortcutCount: number;
|
||||
ratioPerNode: number;
|
||||
witnessSearches: number;
|
||||
witnessBudgetHits: number;
|
||||
};
|
||||
}
|
||||
export const CH_LAMBDA_DELETED_NEIGHBOURS = 1;
|
||||
export const CH_WPS_MAX_SETTLED = 60;
|
||||
export const CH_WPS_SETTLED_PER_DEGREE = 4;
|
||||
export const CH_SHORTCUT_EXPLOSION_RATIO = 4;
|
||||
export function contractGraph(input: ContractionInput): ContractionResult;
|
||||
|
||||
// routing/src/graph.ts
|
||||
export interface ChTablesInput {
|
||||
rank: Uint32Array;
|
||||
shortcutStart: Uint32Array;
|
||||
shortcutTarget: Uint32Array;
|
||||
shortcutSeconds: Float32Array;
|
||||
shortcutVia: Uint32Array;
|
||||
}
|
||||
export interface RoadGraph {
|
||||
chShortcutCount: number;
|
||||
chRank: Uint32Array;
|
||||
shortcutStart: Uint32Array;
|
||||
shortcutTarget: Uint32Array;
|
||||
shortcutSeconds: Float32Array;
|
||||
shortcutVia: Uint32Array;
|
||||
}
|
||||
```
|
||||
|
||||
ARG3 fork contract to implement exactly:
|
||||
```text
|
||||
header 72 bytes, little-endian:
|
||||
0 magic u32 = 0x41524733 ("ARG3")
|
||||
4 version u32 = 3
|
||||
8 nodeCount u32
|
||||
12 edgeCount u32
|
||||
16 nameCount u32
|
||||
20 namePoolLen u32
|
||||
24 bbox 4 x f64
|
||||
56 maxSpeedKmh f32
|
||||
60 chShortcutCount u32 (0 -> decode null)
|
||||
64 reserved0 u32
|
||||
68 reserved1 u32
|
||||
sections:
|
||||
lat[N] ++ lng[N] ++ edgeStart[N+1] ++ edgeTarget[E] ++ edgeFlags[E]
|
||||
++ edgeSeconds[E] ++ edgeNameId[E] ++ nameOffset[M+1] ++ namePool
|
||||
++ chRank[N] ++ shortcutStart[N+1] ++ shortcutTarget[S]
|
||||
++ shortcutSeconds[S] ++ shortcutVia[S]
|
||||
```
|
||||
</interfaces>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>task 1: build the pure CH construction core with RED-first invariants</name>
|
||||
<files>routing/src/contraction.ts, routing/test/contraction.test.mjs</files>
|
||||
<read_first>
|
||||
- routing/src/landmark.ts
|
||||
- routing/src/graph.ts
|
||||
- routing/test/landmark.test.mjs
|
||||
- .planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-RESEARCH.md
|
||||
- .planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-PATTERNS.md
|
||||
</read_first>
|
||||
<behavior>
|
||||
- Case C1: contractGraph over a 5-node directed fixture returns distinct ranks covering exactly [0, nodeCount).
|
||||
- Case C2: every emitted shortcut's seconds equal the sum of the weights of the two edges it replaced.
|
||||
- Case C3: every shortcut is upward, with rank(from) > rank(via) and rank(to) > rank(via).
|
||||
- Case C4: a hand-computed 3-4 node fixture yields its literal expected contraction order and shortcut set.
|
||||
- Case C5: the witness search sees only the overlay graph; a previously contracted node cannot witness a shortcut.
|
||||
- Case C6: the shortcut-explosion tripwire fails loud above CH_SHORTCUT_EXPLOSION_RATIO * nodeCount and passes for a healthy graph.
|
||||
- Case C7: two calls on identical input produce byte-identical rank and shortcut arrays.
|
||||
</behavior>
|
||||
<action>
|
||||
Write `routing/test/contraction.test.mjs` first using the existing lazy-import harness, `node:assert/strict`, and hand-built vectors. Then create `routing/src/contraction.ts` as a pure module with zero value imports, no I/O, no logging, and no storage.
|
||||
|
||||
Implement these exact constants and exports:
|
||||
- `CH_LAMBDA_DELETED_NEIGHBOURS = 1`
|
||||
- `CH_WPS_MAX_SETTLED = 60`
|
||||
- `CH_WPS_SETTLED_PER_DEGREE = 4`
|
||||
- `CH_SHORTCUT_EXPLOSION_RATIO = 4`
|
||||
- `ContractionInput { nodeCount, edges: Array<{from,to,seconds,flags}> }`
|
||||
- `ContractionResult { rank, shortcutStart, shortcutTarget, shortcutSeconds, shortcutVia, stats }`
|
||||
- `contractGraph(input): ContractionResult`
|
||||
|
||||
Implement the bottom-up CH algorithm exactly:
|
||||
1. Validate node ids and finite non-negative edge seconds before constructing CSR views. An edge with `flags & 1` is blocked in every direction and must never be relaxed.
|
||||
2. Compute priority as `edgeDifference(u) + CH_LAMBDA_DELETED_NEIGHBOURS * deletedNeighbours(u)`. Use a lazy binary priority queue. When a popped priority is stale, recompute it and requeue if it improved. Break priority ties by the lower node id.
|
||||
3. Assign `rank[u]` as the strictly increasing contraction position, never as the priority value. Thus rank is a total order over all nodes.
|
||||
4. For each node `u` being contracted, enumerate overlay in-neighbours `a` and overlay out-neighbours `b`. The candidate shortcut is directed `a -> b`, with seconds `w(a,u) + w(u,b)` and `via = u`.
|
||||
5. Run each witness search on the overlay graph with `u` removed and all previously contracted nodes absent. Bound it by both the candidate distance and `max(CH_WPS_MAX_SETTLED, CH_WPS_SETTLED_PER_DEGREE * localDegree)`. If the witness search is bounded out or finds no path within the candidate distance, emit the shortcut; bounded witness searches may add superfluous shortcuts but must not remove a required one.
|
||||
6. Remove `u` and its incident overlay edges before the next witness search. Emit shortcuts in deterministic order: ascending `from`, then ascending `to`.
|
||||
7. Build shortcut CSR arrays where `shortcutStart` has length `nodeCount + 1` and every source's shortcuts are contiguous.
|
||||
8. Fail loud if `shortcutCount > CH_SHORTCUT_EXPLOSION_RATIO * nodeCount`, with a message naming the ratio and the measured count.
|
||||
9. Populate `stats` with `shortcutCount`, `ratioPerNode = shortcutCount / nodeCount`, `witnessSearches`, and `witnessBudgetHits`.
|
||||
|
||||
Determinism is mandatory: no PRNG, no `Date`, no Map iteration order, no reliance on object key order. Identical input arrays must yield identical output arrays. Do not use any external CH library and do not add a dependency.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npm run routing:test -- --test-name-pattern='C[1-7]' && npm run routing:typecheck</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `routing/src/contraction.ts` contains `export function contractGraph` and all four `CH_*` constants with the exact values 1, 60, 4, and 4.
|
||||
- `routing/test/contraction.test.mjs` contains cases whose names include C1, C2, C3, C4, C5, C6, and C7.
|
||||
- `npm run routing:test` exits 0 with no FAIL lines for the C-series cases.
|
||||
- `npm run routing:typecheck` exits 0.
|
||||
- `grep -v '^[[:space:]]*\*' routing/src/contraction.ts | grep -v '^[[:space:]]*//' | grep -c 'Date\|Math.random'` equals 0.
|
||||
</acceptance_criteria>
|
||||
<done>CH construction is deterministic, rank is a strict total order, shortcut weights and upwardness are proven, the overlay-only witness law is pinned, and the explosion tripwire fails loud.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>task 2: implement ARG3 codec persistence and the uphill bidirectional CH query</name>
|
||||
<files>routing/src/graph.ts, routing/src/ch-query.ts, routing/src/astar.ts, routing/src/index.ts, routing/src/landmark.ts, routing/test/ch-query.test.mjs, routing/test/graph.test.mjs, routing/test/astar.test.mjs, routing/test/landmark.test.mjs</files>
|
||||
<read_first>
|
||||
- routing/src/graph.ts
|
||||
- routing/src/astar.ts
|
||||
- routing/src/landmark.ts
|
||||
- routing/src/index.ts
|
||||
- routing/test/graph.test.mjs
|
||||
- routing/test/astar.test.mjs
|
||||
- routing/test/rollout.test.mjs
|
||||
- .planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-RESEARCH.md
|
||||
</read_first>
|
||||
<behavior>
|
||||
- Case Q1: the CH query equals brute-force Dijkstra for every pair of the 25-node mini-graph, with path endpoints equal to the requested origin and goal.
|
||||
- Case Q2: a directed ring/chord/spur fixture matches the Dijkstra oracle in both directions for every pair and never traverses a bit0 edge.
|
||||
- Case Q3: the returned seconds equal a fresh sum over the returned original-edge path and never equal a sum of shortcut weights.
|
||||
- Case Q4: an unreachable pair returns null.
|
||||
- Case Q5: comment-stripped `astar.ts` contains none of `landmarkTo`, `landmarkFrom`, `cleanColumnsFor`, `LANDMARK_SENTINEL`; `routing/src/landmark.ts` and `routing/test/landmark.test.mjs` are absent; the index exports no landmark symbols.
|
||||
- Case G1: ARG3 round-trips rank and shortcut CSR exactly through zero-copy views.
|
||||
- Case G2: ARG1 bytes and ARG2 bytes both decode to null; a version-2 tamper decodes to null.
|
||||
- Case G3: truncated, hostile-count, and `chShortcutCount = 0` buffers decode to null before any typed-array allocation.
|
||||
- Case G4: the committed ARG3 mini-graph has 25 nodes, 80 edges, a rank of length 25, and at least one shortcut.
|
||||
</behavior>
|
||||
<action>
|
||||
Replace the ARG2 codec in `routing/src/graph.ts` with the ARG3 codec described in the interfaces block. Use `GRAPH_MAGIC = 0x41524733` and `GRAPH_VERSION = 3`. Keep `HEADER_SIZE = 72` so f64 views remain 8-byte aligned. Replace landmark header fields with `chShortcutCount` u32 @60 and reserved u32 @64/@68. Remove `landmarkCount`, `landmarkQuantumS`, `landmarkTo`, `landmarkFrom`, and `landmarkNodes` from `RoadGraph`, `LandmarkTablesInput`, and `GraphInput`; replace them with `ChTablesInput` and `GraphInput.ch`.
|
||||
|
||||
In `layoutFor`, calculate the CH suffix in this order and use 4-byte alignment before every u32/f32 section: `chRank = (namePool + namePoolLen + 3) & ~3`, then `shortcutStart = chRank + 4*nodeCount`, `shortcutTarget = shortcutStart + 4*(nodeCount+1)`, `shortcutSeconds = shortcutTarget + 4*shortcutCount`, `shortcutVia = shortcutSeconds + 4*shortcutCount`, `total = shortcutVia + 4*shortcutCount`. Run the exact-size arithmetic check before allocating any view. `chShortcutCount === 0` must return null even when the byte size matches. `decodeGraph` must never throw for malformed bytes and must never return a half-built graph.
|
||||
|
||||
Create `routing/src/ch-query.ts` with the pure uphill bidirectional CH query. Its public result shape must be `{ nodes: number[]; seconds: number; distanceM: number }`. The forward search relaxes only edges whose target rank is strictly greater than the source rank. The backward search relaxes only incoming edges whose source rank is strictly greater than the settled node's rank. Use separate forward and backward edge access; never reuse the forward shortcut list in the backward search. Apply the shared traversal law in every scan: `if ((edgeFlags[e] & 1) !== 0) continue;`.
|
||||
|
||||
The stop rule is `if (forwardHeap.peekKey() + backwardHeap.peekKey() >= mu) break;`. Alternating the smaller frontier first is only a work-balance knob and must not affect optimality. Use the edge-based meet probe already present in astar.ts. After reconstructing the parent chains, recursively unpack every shortcut through its `via` field until only original edges remain. THEN compute `seconds` by summing `graph.edgeSeconds[findTraversableEdge(...)]` over consecutive original-edge pairs, and compute `distanceM` by summing haversine over that same unpacked path. Shortcut weights steer the search only; they never enter reported seconds.
|
||||
|
||||
Rewrite `routing/src/astar.ts` so `routeAstar` validates origin/goal, handles origin === goal, and delegates to the CH query for every other request. Preserve `AstarResult`, `deriveSteps`, `classifyTurn`, and all exported turn constants exactly. Delete the ALT machinery: `LANDMARK_SENTINEL_U16`, `cleanColumnsFor`, the `cleanColumnGraphs` memo, `metersTo`, `bound`, `potential`, the ALT/BH search body, and every landmark-table reference. Per D-04 there is exactly one search path.
|
||||
|
||||
Update `routing/src/index.ts` to export the existing public API and additively export `contractGraph`, `ContractionInput`, `ContractionResult`, and the four `CH_*` constants if the query implementation needs them. Export no landmark symbols.
|
||||
|
||||
Delete `routing/src/landmark.ts` and `routing/test/landmark.test.mjs` per D-05. Re-pin `routing/test/graph.test.mjs` for ARG3 and re-point `routing/test/astar.test.mjs` A1-A8 at the CH-backed `routeAstar`; remove or fold the ALT-specific A9-A13 cases into the CH query suite rather than retaining landmark fixtures.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npm run routing:test && npm run routing:typecheck</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `routing/src/graph.ts` contains `GRAPH_MAGIC = 0x41524733` and `GRAPH_VERSION = 3` and contains no `landmarkTo`, `landmarkFrom`, or `landmarkCount`.
|
||||
- `routing/src/ch-query.ts` exists and contains the upward-rank filter, the `peekKey()` stop rule, and shortcut unpacking through `via`.
|
||||
- `routing/src/astar.ts` comment-stripped code contains none of `landmarkTo`, `landmarkFrom`, `cleanColumnsFor`, `LANDMARK_SENTINEL`.
|
||||
- `routing/src/landmark.ts` and `routing/test/landmark.test.mjs` do not exist.
|
||||
- `npm run routing:test` exits 0 and `npm run routing:typecheck` exits 0.
|
||||
- `grep -v '^[[:space:]]*\*' routing/src/astar.ts | grep -v '^[[:space:]]*//' | grep -c 'landmarkTo\|landmarkFrom\|cleanColumnsFor\|LANDMARK_SENTINEL'` equals 0.
|
||||
</acceptance_criteria>
|
||||
<done>ARG3 is the sole binary format, the CH uphill query is the sole search path, ALT code and tables are gone, and reported seconds come from the unpacked original-edge path.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>task 3: rebuild all fixtures and bot graph builders through the ARG3 path</name>
|
||||
<files>routing/test/fixtures/build-mini-graph.mjs, routing/test/fixtures/mini-graph.bin, routing/test/fixtures/mini-graph-arg2.bin, bot/test/webhook.test.mjs, bot/test/guidance-cron.test.mjs</files>
|
||||
<read_first>
|
||||
- routing/test/fixtures/build-mini-graph.mjs
|
||||
- routing/test/fixtures/mini-graph.bin
|
||||
- routing/test/graph.test.mjs
|
||||
- bot/test/webhook.test.mjs
|
||||
- bot/test/guidance-cron.test.mjs
|
||||
- routing/src/contraction.ts
|
||||
- routing/src/graph.ts
|
||||
- .planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-PATTERNS.md
|
||||
</read_first>
|
||||
<behavior>
|
||||
- The committed mini-graph fixture decodes as ARG3 with 25 nodes, 80 edges, and a valid CH section.
|
||||
- The preserved ARG2 fixture decodes to null.
|
||||
- Every bot test graph encodes and decodes through the ARG3 `ch` block and produces the same route behavior as before.
|
||||
</behavior>
|
||||
<action>
|
||||
Before regenerating the mini-graph, copy the current committed `routing/test/fixtures/mini-graph.bin` bytes exactly once to `routing/test/fixtures/mini-graph-arg2.bin`. Add a provenance comment to `build-mini-graph.mjs` stating that `mini-graph-arg2.bin` holds the frozen ARG2-era bytes preserved as the ARG2 decode-rejection vector and is never regenerated by the script, matching the existing `mini-graph-arg1.bin` precedent.
|
||||
|
||||
Replace the landmark stage in `routing/test/fixtures/build-mini-graph.mjs` with a CH stage. Import `contractGraph` from `../../src/contraction.ts`, call it with the 25-node grid's directed edges, and pass its result as `ch:` to `encodeGraph`. Before writing, fail loud unless: `new Set(rank).size === 25`; every rank is in [0,25); the shortcut count is within the expected grid band; every `via` is a node id in [0,25); every shortcut's seconds equal its two constituent edge weights; and the graph remains strongly connected.
|
||||
|
||||
Update `bot/test/webhook.test.mjs`: replace the `landmark.ts` import and `landmarkBlock()` with a `chBlock(nodeCount, edges)` helper that computes `contractGraph` and returns the CH block. Replace every `landmarks: { ... }` argument to `encodeGraph` with `ch: { ... }`. There are three graph builders in this file; all must use the ARG3 path.
|
||||
|
||||
Update `bot/test/guidance-cron.test.mjs`: replace the pinned-anchor 2-node graph builder with a `contractGraph` call and encode with `ch:`. The 2-node graph must contract successfully; no anchor pin is allowed.
|
||||
|
||||
Run `node routing/test/fixtures/build-mini-graph.mjs` to regenerate `mini-graph.bin` as ARG3 and confirm the printed node/edge/shortcut/byte counts.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>node routing/test/fixtures/build-mini-graph.mjs && npm run routing:test && npm test -w bot && npm run bot:typecheck</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `routing/test/fixtures/mini-graph-arg2.bin` exists and `node -e "const fs=require('fs');const b=fs.readFileSync('routing/test/fixtures/mini-graph-arg2.bin');console.log(b.readUInt32LE(4))"` prints `2`.
|
||||
- `node -e "const fs=require('fs');const b=fs.readFileSync('routing/test/fixtures/mini-graph.bin');console.log(b.readUInt32LE(0).toString(16),b.readUInt32LE(4))"` prints `41524733 3`.
|
||||
- `grep -v '^[[:space:]]*\*' bot/test/webhook.test.mjs | grep -v '^[[:space:]]*//' | grep -c 'landmark'` equals 0.
|
||||
- `grep -v '^[[:space:]]*\*' bot/test/guidance-cron.test.mjs | grep -v '^[[:space:]]*//' | grep -c 'landmark'` equals 0.
|
||||
- `npm run routing:test`, `npm test -w bot`, and `npm run bot:typecheck` exit 0.
|
||||
</acceptance_criteria>
|
||||
<done>Every committed and bot test graph is ARG3-shaped, the old ARG2 bytes survive only as an immutable rejection vector, and the routing and bot suites stay green.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| R2 object -> Worker decoder | The R2-held road-graph bytes are untrusted to the decoder: corruption, truncation, hostile counts, or misalignment must produce null, never a throw or half-built graph. |
|
||||
| Request -> Worker -> CH query | Untrusted route parameters reach the search only after the existing Bearer claim and bbox validation; the CH query must not broaden that surface. |
|
||||
| Build pipeline -> binary | The Overpass-derived graph is transformed by build-time contraction into the persisted hierarchy; a stale hierarchy against changed topology is a build-artifact discipline problem. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-08.1-01 | Tampering / DoS | `decodeGraph` ARG3 header and section arithmetic | mitigate | `layoutFor` computes every offset in one place; exact-size validation runs before any typed-array allocation; `chShortcutCount === 0` returns null; malformed bytes return null rather than throwing. Pinned by graph.test.mjs G3/G4/G11 successors. |
|
||||
| T-08.1-02 | DoS | Misaligned `Float32Array`/`Uint32Array` CH views | mitigate | Keep `HEADER_SIZE = 72` for 8-byte f64 alignment and align the CH suffix with `(namePool + namePoolLen + 3) & ~3`; G1 round-trip and hostile-buffer tests decode the committed ARG3 fixture without RangeError. |
|
||||
| T-08.1-03 | Tampering | Directed shortcut confusion across oneway edges | mitigate | Shortcuts are directed only; forward and backward searches use separate edge access; bit0 edges are never relaxed in forward, backward, or witness scans; the directed ring/chord/spur sweep compares both directions with the Dijkstra oracle. |
|
||||
| T-08.1-04 | Tampering | Stale CH hierarchy after topology or weight changes | mitigate | ARG3 is rebuilt and re-uploaded as one whole artifact under the unchanged key; `docs/ROAD-GRAPH.md` records that the hierarchy is valid only for the topology and weights it was built against. |
|
||||
| T-08.1-05 | DoS | Unbounded build-time witness search | mitigate | Witness search is bounded by candidate distance and `max(CH_WPS_MAX_SETTLED, CH_WPS_SETTLED_PER_DEGREE * localDegree)`; build prints witness-search and budget-hit counts. |
|
||||
| T-08.1-06 | Tampering / DoS | Shortcut explosion inflating binary and query cost | mitigate | `CH_SHORTCUT_EXPLOSION_RATIO = 4` fails the build loud; the existing 1-80 MB envelope remains enforced; no fixed payload ceiling is invented. |
|
||||
| T-08.1-07 | Information Disclosure | Coordinates, route payloads, or user identity in logs | accept | The phase adds no logging and changes no `/api/route` response shape; existing category-only log law and Bearer-claim identity remain unchanged. No new log statement may be introduced in routing modules. |
|
||||
| T-08.1-08 | Spoofing / Tampering | Dormant ALT path or second decoder producing a different answer | mitigate | D-04/D-05 deletion is pinned structurally: no ALT tokens in astar.ts, landmark.ts absent, no landmark exports, one decode path in the Worker; routeAstar delegates every request to the CH query. |
|
||||
| T-08.1-09 | DoS | CPU exhaustion on the request path | mitigate | The uphill query uses the `peekKey()` stop rule and returns null for unreachable pairs; the local CPU gate and tightened drift guard in Plan 02 are the enforcement point. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
Run the full automated suite after this plan:
|
||||
|
||||
```bash
|
||||
npm run routing:test && npm test -w bot && npm run routing:typecheck && npm run bot:typecheck && npm run web:typecheck
|
||||
```
|
||||
|
||||
Also verify structural deletion and the one-path law:
|
||||
|
||||
```bash
|
||||
test ! -e routing/src/landmark.ts && test ! -e routing/test/landmark.test.mjs
|
||||
grep -v '^[[:space:]]*\*' routing/src/astar.ts | grep -v '^[[:space:]]*//' | grep -c 'landmarkTo\|landmarkFrom\|cleanColumnsFor\|LANDMARK_SENTINEL'
|
||||
grep -c 'decodeGraph(' bot/src/routing/worker-router.ts
|
||||
```
|
||||
|
||||
Expected: routing and bot suites green; all typechecks green; structural ALT count 0; Worker decodeGraph count 1.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- CH construction and query behavior is proven by the routing unit suite, including a hand-computed fixture, overlay witness law, oneway direction, and original-edge seconds.
|
||||
- ARG3 is the only encoded/decoded format; ARG1 and ARG2 decode to null through the existing fail-honest path.
|
||||
- `routeAstar` is the sole exported search and is CH-backed for every request; no ALT runtime code or landmark payload remains.
|
||||
- The committed mini-graph and both bot graph builders are ARG3-shaped and produced through the same encode path.
|
||||
- `npm run routing:test`, `npm test -w bot`, and all three typechecks pass.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-01-SUMMARY.md`
|
||||
</output>
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
---
|
||||
phase: 08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on:
|
||||
- 08.1-01
|
||||
files_modified:
|
||||
- routing/scripts/build-road-graph.mjs
|
||||
- routing/scripts/measure-route-cpu.mjs
|
||||
- routing/scripts/measure-optimality-gate.mjs
|
||||
- routing/scripts/measure-astar-oracle-sweep.mjs
|
||||
- scripts/lib/budget-gates.mjs
|
||||
- scripts/test/budget-audit.test.mjs
|
||||
- routing/test/rollout.test.mjs
|
||||
autonomous: true
|
||||
requirements:
|
||||
- D-01
|
||||
- D-02
|
||||
- D-03
|
||||
- D-08
|
||||
- D-09
|
||||
- D-13
|
||||
must_haves:
|
||||
truths:
|
||||
- "The production graph build runs CH ordering, contraction, and shortcut encoding and prints measured bytes, shortcut ratio, witness counts, and wall-clock without inventing a fixed payload ceiling."
|
||||
- "The existing optimality gate and 200-pair seeded Dijkstra-oracle sweep run unchanged against the ARG3 binary and report zero optimality violations."
|
||||
- "The local route-CPU gate prints the CH p95 against the unchanged 8 ms product budget and is the only SC-1 closing measurement."
|
||||
- "The drift guard is re-anchored to approximately two times the measured CH p95 only when the c3 class retires, and the constant plus its audit mirror move together."
|
||||
- "The Worker and upload path remain format-agnostic: one decode call, no version branch, no format token in the upload script."
|
||||
artifacts:
|
||||
- path: "routing/scripts/build-road-graph.mjs"
|
||||
provides: "Build-time CH preprocessing stage and measured ARG3 statistics print"
|
||||
- path: "routing/scripts/measure-route-cpu.mjs"
|
||||
provides: "Unchanged local 25-route and extended 100-route CH CPU measurement gates"
|
||||
- path: "scripts/lib/budget-gates.mjs"
|
||||
provides: "CPU verdict gate with D-03 drift-guard constant and CH regime wording"
|
||||
- path: "scripts/test/budget-audit.test.mjs"
|
||||
provides: "CPU3 mirror vector pinned to the CH verdict"
|
||||
key_links:
|
||||
- from: "routing/scripts/build-road-graph.mjs"
|
||||
to: "routing/src/contraction.ts"
|
||||
via: "contractGraph over the compacted directed edges, then encodeGraph ch block"
|
||||
pattern: "contractGraph|encodeGraph"
|
||||
- from: "routing/scripts/measure-route-cpu.mjs"
|
||||
to: "routing/src/astar.ts"
|
||||
via: "routeAstar now delegates to the CH query"
|
||||
pattern: "routeAstar|astar"
|
||||
- from: "scripts/lib/budget-gates.mjs"
|
||||
to: "scripts/test/budget-audit.test.mjs"
|
||||
via: "ROUTE_CPU_DRIFT_GUARD_MS and its CPU3 evidence mirror"
|
||||
pattern: "ROUTE_CPU_DRIFT_GUARD_MS|CPU3"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Wire the ARG3 binary into the real build and measurement campaign, then close SC-1 from measured local evidence with an honest retire-or-shrink verdict.
|
||||
|
||||
Purpose: The CH implementation is only useful if the production binary carries the hierarchy and the same evidence class that failed Phase 8 measures whether it succeeded. D-01 forbids retiring the c3 class on failing numbers; D-02 forbids substituting a deployed probe.
|
||||
Output: CH build stage, measured ARG3 build, re-run optimality/oracle/CPU gates, and the tightened or deliberately unchanged drift guard.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.config/opencode/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.config/opencode/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-CONTEXT.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-RESEARCH.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-PATTERNS.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-VALIDATION.md
|
||||
@routing/scripts/build-road-graph.mjs
|
||||
@routing/scripts/measure-route-cpu.mjs
|
||||
@routing/scripts/measure-optimality-gate.mjs
|
||||
@routing/scripts/measure-astar-oracle-sweep.mjs
|
||||
@scripts/lib/budget-gates.mjs
|
||||
@scripts/test/budget-audit.test.mjs
|
||||
@routing/test/rollout.test.mjs
|
||||
</context>
|
||||
|
||||
<interfaces>
|
||||
Contracts consumed from Plan 01:
|
||||
```typescript
|
||||
// routing/src/contraction.ts
|
||||
export function contractGraph(input: ContractionInput): ContractionResult;
|
||||
// ContractionResult.stats: { shortcutCount, ratioPerNode, witnessSearches, witnessBudgetHits }
|
||||
```
|
||||
|
||||
Measurement constants to preserve or re-anchor:
|
||||
```javascript
|
||||
// routing/scripts/measure-route-cpu.mjs
|
||||
const P95_BUDGET_MS = 8; // MUST remain exactly 8 and appear exactly once
|
||||
const ROUTE_COUNT = 25;
|
||||
const EXTENDED_PAIR_COUNT = 100;
|
||||
|
||||
// routing/scripts/measure-optimality-gate.mjs
|
||||
const OPTIMALITY_EPSILON_PCT = 0.1;
|
||||
const REQUIRED_BASELINE_ARG_VERSION = 1; // the frozen record is immutable
|
||||
|
||||
// routing/scripts/measure-astar-oracle-sweep.mjs
|
||||
const SWEEP_PAIR_COUNT = 200;
|
||||
const SWEEP_SEED = 0x08f1;
|
||||
const ORACLE_EPSILON_S = 1e-6;
|
||||
|
||||
// scripts/lib/budget-gates.mjs
|
||||
export const P95_BUDGET_MS = 8;
|
||||
export const ROUTE_CPU_DRIFT_GUARD_MS = <ceil(2 * measured CH p95)>; // only if c3 retires
|
||||
```
|
||||
</interfaces>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>task 1: replace the ALT build stage with measured CH preprocessing</name>
|
||||
<files>routing/scripts/build-road-graph.mjs</files>
|
||||
<read_first>
|
||||
- routing/scripts/build-road-graph.mjs
|
||||
- routing/src/contraction.ts
|
||||
- routing/src/graph.ts
|
||||
- routing/test/fixtures/build-mini-graph.mjs
|
||||
- .planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-RESEARCH.md
|
||||
</read_first>
|
||||
<behavior>
|
||||
- The build no longer imports landmark.ts and has no --landmarks option.
|
||||
- The build calls contractGraph and encodes the CH block through encodeGraph.
|
||||
- The build prints contracted node count, shortcut count, ratio per node, witness search count, witness budget hits, CH-section bytes, total bytes, and wall-clock.
|
||||
- The existing 1-80 MB envelope gate still fails before any write; no new fixed payload ceiling exists.
|
||||
</behavior>
|
||||
<action>
|
||||
Remove the `--landmarks` argument parsing, `LANDMARK_COUNT_MIN`, `LANDMARK_COUNT_MAX`, `NODES_PER_LANDMARK`, `LANDMARK_MIN_COVERAGE_FRACTION`, and `LANDMARK_COUNT_OVERRIDE_MAX` constants and their usage.
|
||||
|
||||
Remove the ALT preprocessing block and replace it with one CH stage. Import `contractGraph` from `../src/contraction.ts`. Project the compacted directed edges into `{ from, to, seconds, flags }` and call `contractGraph({ nodeCount: cLat.length, edges })`. If `contractGraph` throws, fail with a message naming the stage and the thrown reason, then exit 1 before any write. Pass the resulting CH data to `encodeGraph` through the `ch:` input field, never a `landmarks:` field.
|
||||
|
||||
Measure the build wall-clock with `process.hrtime.bigint()` around the CH stage and the encode. Print a `CH stats:` block containing exactly these labeled measurements:
|
||||
- `contracted nodes: <N>`
|
||||
- `shortcuts: <S> (<ratio> per node)`
|
||||
- `witness searches: <W> (<H> hit the settle budget)`
|
||||
- `chRank bytes: <4*N>`
|
||||
- `shortcut bytes: <12*S>`
|
||||
- `build wall-clock: <ms> ms`
|
||||
- `total binary: <MB> (<bytes> B)`
|
||||
|
||||
Do not add an ARG3 byte ceiling. Keep the existing enforced `MIN_BINARY_BYTES` floor and `MAX_BINARY_BYTES` ceiling unchanged. Keep collect-then-write semantics: any gate failure exits 1 with the previous binary untouched.
|
||||
|
||||
Update `--check <file>` so it prints CH facts instead of landmark facts: nodeCount, edgeCount, nameCount, maxSpeedKmh, bbox, bytes, chShortcutCount, shortcut count/ratio, and rank length. It must not import landmark.ts.
|
||||
|
||||
Update the script header's usage text to remove `--landmarks` and describe the CH stage.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>node --check routing/scripts/build-road-graph.mjs && grep -v '^[[:space:]]*\*' routing/scripts/build-road-graph.mjs | grep -v '^[[:space:]]*//' | grep -c 'landmark\|--landmarks' || test $? -eq 1</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `grep -v '^[[:space:]]*\*' routing/scripts/build-road-graph.mjs | grep -v '^[[:space:]]*//' | grep -c 'landmark'` equals 0.
|
||||
- `grep -v '^[[:space:]]*\*' routing/scripts/build-road-graph.mjs | grep -v '^[[:space:]]*//' | grep -c 'contractGraph'` is at least 1.
|
||||
- `grep -v '^[[:space:]]*\*' routing/scripts/build-road-graph.mjs | grep -v '^[[:space:]]*//' | grep -c 'build wall-clock\|witness searches\|shortcuts:'` is at least 1.
|
||||
- `node routing/scripts/build-road-graph.mjs --check routing/test/fixtures/mini-graph.bin` exits 0 and prints `chShortcutCount`.
|
||||
</acceptance_criteria>
|
||||
<done>The production build emits ARG3 through the one CH module and prints every D-08/D-09 measurement without a payload ceiling.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>task 2: re-pin the existing gates for ARG3 and preserve the one-path rollout locks</name>
|
||||
<files>routing/scripts/measure-optimality-gate.mjs, routing/scripts/measure-astar-oracle-sweep.mjs, routing/scripts/measure-route-cpu.mjs, routing/test/rollout.test.mjs</files>
|
||||
<read_first>
|
||||
- routing/scripts/measure-optimality-gate.mjs
|
||||
- routing/scripts/measure-astar-oracle-sweep.mjs
|
||||
- routing/scripts/measure-route-cpu.mjs
|
||||
- routing/test/rollout.test.mjs
|
||||
- routing/src/astar.ts
|
||||
- .planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-VALIDATION.md
|
||||
</read_first>
|
||||
<behavior>
|
||||
- The optimality gate still rejects a baseline whose argVersion is not 1 and still mirrors the record's 0.1 percent epsilon.
|
||||
- The oracle sweep still runs 200 compared pairs with seed 0x08f1 and epsilon 1e-6, classifying SUBOPTIMAL, ORACLE-MISMATCH, and class breaks.
|
||||
- The CPU gate still measures 25 routes plus an optional 100-route extended run against P95_BUDGET_MS 8 and prints p95.
|
||||
- The upload script and Worker remain format-agnostic, and the ARG3 magic is added to their banned-token lists.
|
||||
</behavior>
|
||||
<action>
|
||||
In `routing/scripts/measure-optimality-gate.mjs`, keep `OPTIMALITY_EPSILON_PCT = 0.1` and `REQUIRED_BASELINE_ARG_VERSION = 1` unchanged. Change only the decode-failure message so it says the current binary must be ARG3 and names the rebuild command `node routing/scripts/build-road-graph.mjs`. Do not edit `routing/test/fixtures/alt-baseline.json`; it is immutable.
|
||||
|
||||
In `routing/scripts/measure-astar-oracle-sweep.mjs`, keep `SWEEP_PAIR_COUNT = 200`, `SWEEP_SEED = 0x08f1`, `SWEEP_OVERSAMPLE_FACTOR = 3`, `SWEEP_MIN_PAIR_KM = 12`, and `ORACLE_EPSILON_S = 1e-6` unchanged. Update only the graph-missing and decode-failure wording to name ARG3. Keep the symmetric comparison and the three violation classes.
|
||||
|
||||
In `routing/scripts/measure-route-cpu.mjs`, keep `P95_BUDGET_MS = 8`, `ROUTE_COUNT = 25`, `SAMPLE_SEED = 0x06d4`, `EXTENDED_PAIR_COUNT = 100`, and `EXTENDED_SEED = 0x08a1` unchanged. Update the LADDER HISTORY comment with a CH rung line once the measured CH numbers are available. Do not change the measurement logic or the warm-up law. The `P95_BUDGET_MS = 8` string must remain present exactly once in this file.
|
||||
|
||||
In `routing/test/rollout.test.mjs`, add the ARG3 magic `0x41524733` to RO1's banned-token list and to RO2's banned-token list. Keep the Worker pinned to exactly one `decodeGraph` call, no `GRAPH_VERSION`, no graph magic, and no `=== 2`. Keep the upload script's banned-token list free of format awareness.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>npm run routing:test && test "$(grep -c 'P95_BUDGET_MS = 8' routing/scripts/measure-route-cpu.mjs)" -eq 1 && test "$(grep -c 'const calls = code.match(/decodeGraph\(/g)' routing/test/rollout.test.mjs)" -eq 1</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `grep -c 'P95_BUDGET_MS = 8' routing/scripts/measure-route-cpu.mjs` equals 1.
|
||||
- `grep -c 'SWEEP_PAIR_COUNT = 200' routing/scripts/measure-astar-oracle-sweep.mjs` equals 1.
|
||||
- `grep -c 'SWEEP_SEED = 0x08f1' routing/scripts/measure-astar-oracle-sweep.mjs` equals 1.
|
||||
- `grep -c 'ORACLE_EPSILON_S = 1e-6' routing/scripts/measure-astar-oracle-sweep.mjs` equals 1.
|
||||
- `grep -c 'REQUIRED_BASELINE_ARG_VERSION = 1' routing/scripts/measure-optimality-gate.mjs` equals 1.
|
||||
- `grep -c '0x41524733' routing/test/rollout.test.mjs` is at least 2.
|
||||
- `npm run routing:test` exits 0.
|
||||
</acceptance_criteria>
|
||||
<done>The D-13 gates remain unchanged in substance, the failure messages honestly name ARG3, and the Worker/upload one-path laws cover the new magic.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>task 3: run the maintainer measurement campaign and re-anchor the drift guard only on a green verdict</name>
|
||||
<files>scripts/lib/budget-gates.mjs, scripts/test/budget-audit.test.mjs</files>
|
||||
<read_first>
|
||||
- scripts/lib/budget-gates.mjs
|
||||
- scripts/test/budget-audit.test.mjs
|
||||
- routing/scripts/measure-route-cpu.mjs
|
||||
- routing/scripts/measure-optimality-gate.mjs
|
||||
- routing/scripts/measure-astar-oracle-sweep.mjs
|
||||
- docs/BUDGETS.md
|
||||
- docs/KNOWN-LIMITATIONS.md
|
||||
</read_first>
|
||||
<behavior>
|
||||
- The built ARG3 binary decodes and the optimality gate reports zero D03A and zero D03B violations.
|
||||
- The 200-pair oracle sweep reports 0 SUBOPTIMAL, 0 ORACLE-MISMATCH, and 0 class breaks.
|
||||
- The local CPU gate prints the CH p95 for both the 25-route and 100-route extended samples.
|
||||
- If measured CH p95 is below 8 ms, the drift guard becomes ceil(2 * measured p95) and the CPU3 mirror moves in the same commit.
|
||||
- If measured CH p95 is at or above 8 ms, the c3 class remains, the drift guard is not tightened, and the evidence says the class stays.
|
||||
</behavior>
|
||||
<action>
|
||||
Run the maintainer campaign in this exact order and preserve the printed output in the plan summary:
|
||||
|
||||
1. `node routing/scripts/build-road-graph.mjs`
|
||||
2. `node routing/scripts/measure-optimality-gate.mjs`
|
||||
3. `node routing/scripts/measure-astar-oracle-sweep.mjs`
|
||||
4. `node routing/scripts/measure-route-cpu.mjs`
|
||||
5. `node routing/scripts/measure-route-cpu.mjs --extended`
|
||||
6. `node scripts/test/budget-audit.test.mjs`
|
||||
|
||||
The local gate is the sole SC-1 evidence per D-02. Do not add a deployed-Worker CPU probe. Do not change `P95_BUDGET_MS = 8`; do not change `OPTIMALITY_EPSILON_PCT = 0.1`; do not change `ORACLE_EPSILON_S = 1e-6`; do not loosen any gate to manufacture a pass.
|
||||
|
||||
Apply D-01/D-03 conditionally on the printed numbers:
|
||||
- If the 25-route CH p95 is strictly below 8 ms, the c3 class retires. Set `ROUTE_CPU_DRIFT_GUARD_MS` in `scripts/lib/budget-gates.mjs` to `Math.ceil(2 * measuredChP95Ms)` as a literal integer, and update its provenance comment to name the measured CH p95, the date, and the host. In the same commit, update the CPU3 vector in `scripts/test/budget-audit.test.mjs`: it must assert the CH verdict wording and must no longer require the literal `EXECUTED in Phase 8` phrase. Keep the `p95 <number> ms`, `8 ms`, `10 ms free-plan ceiling`, and `GATE FAIL` assertions when the child still exits 1 for an over-budget regression.
|
||||
- If the 25-route CH p95 is at or above 8 ms, keep `ROUTE_CPU_DRIFT_GUARD_MS` at its current value and keep the c3 class. Rewrite only the budget-gates verdict prose so it names the executed-and-measured CH result and states that the class remains because retirement fires only on a passing measurement. Update the CPU3 mirror regex in the same commit to the new wording.
|
||||
|
||||
In both branches, the three-branch verdict structure in `runCpuLadderGate` stays: uncharted regression beyond the drift guard, over-budget-within-band, and within-budget. Keep the honest NOT-RUN behavior for a missing binary.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>node routing/scripts/measure-optimality-gate.mjs && node routing/scripts/measure-astar-oracle-sweep.mjs && node scripts/test/budget-audit.test.mjs</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `node routing/scripts/measure-optimality-gate.mjs` prints `GATE PASS` and `0 violation(s)` for both D03A and D03B.
|
||||
- `node routing/scripts/measure-astar-oracle-sweep.mjs` prints `GATE PASS` and `0 SUBOPTIMAL`, `0 ORACLE-MISMATCH`, `0 class-breaks`.
|
||||
- `node routing/scripts/measure-route-cpu.mjs` prints a `p95: <number> ms` line; the number is recorded verbatim in the plan summary.
|
||||
- `grep -c 'P95_BUDGET_MS = 8' scripts/lib/budget-gates.mjs` equals 1.
|
||||
- `node scripts/test/budget-audit.test.mjs` exits 0.
|
||||
- The summary states explicitly whether the c3 class retired and, if it did, the exact new `ROUTE_CPU_DRIFT_GUARD_MS` value; if it did not, the summary states that the class remains.
|
||||
</acceptance_criteria>
|
||||
<done>The measured CH verdict exists, the same local evidence class decides SC-1, and the drift guard plus its mirror move together only when the numbers justify retirement.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| Overpass response -> build pipeline | External map data enters the graph builder; malformed or hostile data must fail the build before a binary is written. |
|
||||
| Built ARG3 binary -> measurement gates | The gitignored local binary is the artifact under test; a corrupt or stale binary must make gates fail honestly rather than pass. |
|
||||
| Gate output -> budget verdict | Measured p95 and guard constants decide whether the c3 class retires; a mistaken or stale number could retire the class on failing evidence. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-08.1-10 | Tampering / DoS | Overpass-derived graph entering contraction | mitigate | The existing build validates extracted ways, node ids, finite coordinates, and non-empty graphs before contraction; any `contractGraph` throw fails before collect-then-write, leaving the previous binary untouched. |
|
||||
| T-08.1-11 | Tampering | Stale or corrupt local binary passing a gate | mitigate | `decodeGraph` returns null for non-ARG3 bytes and every measurement script fails on null; the gates never substitute a missing measurement for a pass. |
|
||||
| T-08.1-12 | Repudiation | Retiring c3 on failing numbers | mitigate | D-01 is encoded in the conditional action: the guard is re-anchored only when the printed 25-route p95 is strictly below 8 ms; the measured value and verdict must be quoted in the plan summary and later record surfaces. |
|
||||
| T-08.1-13 | Tampering | Constant-mirror drift between guard and audit vector | mitigate | `ROUTE_CPU_DRIFT_GUARD_MS` and the CPU3 evidence mirror are changed in the same commit; CPU2 still pins `P95_BUDGET_MS = 8` exactly once. |
|
||||
| T-08.1-14 | DoS | Unbounded CH build consuming the maintainer machine | mitigate | Witness searches are bounded by distance and settle count; the build prints wall-clock and budget-hit counts; D-09 treats an over-run as an honest measurement, not a hidden failure. |
|
||||
| T-08.1-15 | DoS | Search CPU regression after the re-anchor | mitigate | The tightened drift guard is approximately 2x the measured CH p95, so a regression that consumes the headroom fails the route-cpu gate; the 8 ms product budget remains the binding pass bar. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
Run the full suite and the maintainer campaign:
|
||||
|
||||
```bash
|
||||
npm run routing:test && npm test -w bot && npm run routing:typecheck && npm run bot:typecheck && npm run web:typecheck
|
||||
node scripts/test/budget-audit.test.mjs
|
||||
node routing/scripts/build-road-graph.mjs
|
||||
node routing/scripts/measure-optimality-gate.mjs
|
||||
node routing/scripts/measure-astar-oracle-sweep.mjs
|
||||
node routing/scripts/measure-route-cpu.mjs
|
||||
node routing/scripts/measure-route-cpu.mjs --extended
|
||||
```
|
||||
|
||||
Expected: every suite green; optimality and oracle gates green; both CPU p95 values printed and recorded; the drift guard either tightened to ceil(2 x measured p95) with its mirror updated, or deliberately unchanged with the class remaining.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- The real build produces ARG3 with measured CH statistics and no invented byte ceiling.
|
||||
- The D-03 optimality gate and 200-pair oracle sweep pass against the ARG3 binary without threshold changes.
|
||||
- The local CPU gate prints the CH p95 for both sample widths.
|
||||
- The c3 class is retired only when the measured p95 is below 8 ms; otherwise it remains with an honest record.
|
||||
- The drift guard and its audit mirror agree, and the Worker/upload path remains format-agnostic.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-02-SUMMARY.md`
|
||||
</output>
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
---
|
||||
phase: 08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 3
|
||||
depends_on:
|
||||
- 08.1-02
|
||||
files_modified:
|
||||
- docs/ROAD-GRAPH.md
|
||||
- docs/BUDGETS.md
|
||||
- docs/KNOWN-LIMITATIONS.md
|
||||
- docs/ALT-DIRECTION-SPLIT.md
|
||||
- routing/README.md
|
||||
autonomous: true
|
||||
requirements:
|
||||
- D-01
|
||||
- D-03
|
||||
- D-06
|
||||
- D-08
|
||||
- D-09
|
||||
- D-10
|
||||
- D-13
|
||||
must_haves:
|
||||
truths:
|
||||
- "The public ARG3 fork contract is documented field-by-field so a browser/webview consumer can decode the same asset without Node-specific assumptions."
|
||||
- "The budget and known-limitations records state the measured CH verdict and never claim success when the measurement failed."
|
||||
- "The direction-split potentials experiment is closed as superseded while its measured facts remain intact."
|
||||
- "The repository's routing module map describes the CH data plane and contains no active ALT engine claim."
|
||||
artifacts:
|
||||
- path: "docs/ROAD-GRAPH.md"
|
||||
provides: "ARG3 header/section contract, CH build facts, and CPU ladder record"
|
||||
- path: "docs/BUDGETS.md"
|
||||
provides: "Measured CH CPU verdict and retire-or-shrink c3 record"
|
||||
- path: "docs/KNOWN-LIMITATIONS.md"
|
||||
provides: "Honest cross-metro status after the CH measurement"
|
||||
- path: "docs/ALT-DIRECTION-SPLIT.md"
|
||||
provides: "Superseded closing header with historical measured facts preserved"
|
||||
key_links:
|
||||
- from: "docs/ROAD-GRAPH.md"
|
||||
to: "routing/src/graph.ts"
|
||||
via: "documented ARG3 header offsets and section order match the codec authority"
|
||||
pattern: "ARG3|chShortcutCount|chRank"
|
||||
- from: "docs/BUDGETS.md"
|
||||
to: "routing/scripts/measure-route-cpu.mjs"
|
||||
via: "quoted measured p95 and 8 ms budget come from the local gate output"
|
||||
pattern: "p95|8 ms"
|
||||
- from: "docs/ALT-DIRECTION-SPLIT.md"
|
||||
to: "docs/BUDGETS.md"
|
||||
via: "historical ALT facts retained while the post-CH verdict lives in the budget record"
|
||||
pattern: "Phase 8|superseded"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Close the phase with an honest public record: the ARG3 fork contract, the measured CH build and CPU facts, the retire-or-shrink c3 verdict, and the superseded direction-split experiment.
|
||||
|
||||
Purpose: The phase's value depends on truthful engineering records. A passing implementation with a stale or optimistic document would preserve the exact failure mode Phase 8 exposed.
|
||||
Output: Updated road-graph contract, budgets, known limitations, ALT experiment closure, and routing module map.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.config/opencode/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.config/opencode/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-CONTEXT.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-RESEARCH.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-PATTERNS.md
|
||||
@.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-VALIDATION.md
|
||||
@docs/ROAD-GRAPH.md
|
||||
@docs/BUDGETS.md
|
||||
@docs/KNOWN-LIMITATIONS.md
|
||||
@docs/ALT-DIRECTION-SPLIT.md
|
||||
@routing/README.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>task 1: document the ARG3 fork contract and measured build facts</name>
|
||||
<files>docs/ROAD-GRAPH.md, routing/README.md</files>
|
||||
<read_first>
|
||||
- docs/ROAD-GRAPH.md
|
||||
- routing/README.md
|
||||
- routing/src/graph.ts
|
||||
- routing/scripts/build-road-graph.mjs
|
||||
- routing/scripts/measure-route-cpu.mjs
|
||||
- .planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-RESEARCH.md
|
||||
</read_first>
|
||||
<action>
|
||||
In `docs/ROAD-GRAPH.md`, replace the ARG2 layout section with an ARG3 section that is the public fork contract required by D-10. Document these exact facts:
|
||||
- magic `0x41524733` ("ARG3"), version 3, header size 72 bytes, little-endian only.
|
||||
- The header field table with byte offsets: magic @0 u32, version @4 u32, nodeCount @8 u32, edgeCount @12 u32, nameCount @16 u32, namePoolLen @20 u32, bbox @24 as four f64 values, maxSpeedKmh @56 f32, chShortcutCount @60 u32, reserved0 @64 u32, reserved1 @68 u32.
|
||||
- The section order: lat[N] ++ lng[N] ++ edgeStart[N+1] ++ edgeTarget[E] ++ edgeFlags[E] ++ edgeSeconds[E] ++ edgeNameId[E] ++ nameOffset[M+1] ++ namePool ++ chRank[N] ++ shortcutStart[N+1] ++ shortcutTarget[S] ++ shortcutSeconds[S] ++ shortcutVia[S].
|
||||
- The alignment law: header 72 bytes keeps f64 views 8-byte aligned; every u32/f32 section starts at a 4-byte boundary using `(offset + 3) & ~3`.
|
||||
- The decode law: only exact ARG3 bytes decode; `chShortcutCount === 0`, any size mismatch, and ARG1/ARG2 bytes return null and route to the designed router-pending state. There is no version fallback branch.
|
||||
- The semantics of each CH field: `chRank[u]` is the contraction position (0 contracted first, a strict total order); `shortcutStart` is the shortcut CSR row pointer array; `shortcutTarget` is the shortcut head; `shortcutSeconds` is the shortcut's f32 seconds; `shortcutVia` is the contracted midpoint used to unpack the shortcut recursively.
|
||||
- The phone-first browser-decodability statement: little-endian typed arrays only, no Node-specific alignment assumptions, so Phase 11's webview can decode this same asset.
|
||||
- The build-artifact discipline: the hierarchy is valid only for the exact topology and edge weights it was built against; a graph edit requires a full rebuild and re-upload under the unchanged key `kl-roadgraph.v1.bin`.
|
||||
|
||||
In the same file, update the CPU ladder table and key-facts table to include the CH rung with the measured values from Plan 02: contracted node count, shortcut count and ratio per node, witness search count and budget hits, CH-section bytes, total ARG3 bytes, build wall-clock, 25-route p95, and extended 100-route p95. State the verdict as measured: retired below 8 ms, or the c3 class remains. Do not describe an unmeasured or projected number as measured.
|
||||
|
||||
In `routing/README.md`, update the module map to list `contraction.ts` as the build-time CH core and `ch-query.ts` as the request-time uphill query, remove `landmark.ts` if present, and describe `graph.ts` as the ARG3 codec. Do not add a new dependency or install instruction.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>grep -q 'ARG3' docs/ROAD-GRAPH.md && grep -q 'chShortcutCount' docs/ROAD-GRAPH.md && grep -q 'shortcutVia' docs/ROAD-GRAPH.md && grep -q 'contraction.ts' routing/README.md && ! grep -q 'landmark.ts' routing/README.md</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `docs/ROAD-GRAPH.md` contains `ARG3`, `0x41524733`, `chShortcutCount`, `chRank`, `shortcutStart`, `shortcutTarget`, `shortcutSeconds`, `shortcutVia`, and `72`.
|
||||
- `docs/ROAD-GRAPH.md` contains the byte offset `60` for `chShortcutCount` and `68` for `reserved1`.
|
||||
- `docs/ROAD-GRAPH.md` contains the measured p95 values copied from Plan 02's recorded gate output.
|
||||
- `routing/README.md` contains `contraction.ts` and `ch-query.ts` and contains no `landmark.ts`.
|
||||
</acceptance_criteria>
|
||||
<done>A browser-oriented fork contract fully describes ARG3, and the routing module map reflects the CH data plane truthfully.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>task 2: record the measured c3 verdict and close the direction-split experiment</name>
|
||||
<files>docs/BUDGETS.md, docs/KNOWN-LIMITATIONS.md, docs/ALT-DIRECTION-SPLIT.md</files>
|
||||
<read_first>
|
||||
- docs/BUDGETS.md
|
||||
- docs/KNOWN-LIMITATIONS.md
|
||||
- docs/ALT-DIRECTION-SPLIT.md
|
||||
- docs/ROAD-GRAPH.md
|
||||
- .planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-CONTEXT.md
|
||||
- .planning/phases/08-alt-landmark-preprocessing-routing-backend-v2/08-05-SUMMARY.md
|
||||
</read_first>
|
||||
<action>
|
||||
Update `docs/BUDGETS.md` section c3 using the measured outputs from Plan 02. The new record must include:
|
||||
- The CH build facts: contracted nodes, shortcut count, ratio per node, witness searches and budget hits, CH-section bytes, total ARG3 bytes, and build wall-clock.
|
||||
- The CH CPU facts: the 25-route p95 and the extended 100-route p95 exactly as printed by the local gate, alongside the unchanged 8 ms product budget and the 10 ms free-plan ceiling.
|
||||
- The verdict in D-01's exact terms: if p95 is below 8 ms, write that the c3 class RETIRED because the measured CH p95 landed under the 8 ms budget; if p95 is at or above 8 ms, write that the c3 class REMAINS and that the $0 lever ladder is documented as a dead end, not a failure.
|
||||
- The new drift guard value if the class retired, described as approximately 2x the measured CH p95. If it did not retire, state that the previous guard remains.
|
||||
- The optimality evidence: D-03 gate green within ±0.1% and the 200-pair oracle sweep with zero SUBOPTIMAL, zero ORACLE-MISMATCH, and zero class breaks.
|
||||
- The local-only evidence scope per D-02, including the documented host variance, and no claim of a deployed-Worker measurement.
|
||||
|
||||
Update `docs/KNOWN-LIMITATIONS.md` so the cross-metro section reflects the measured CH state: remove the active ALT roadmap pointer, replace it with the CH verdict, and state whether cross-metro routing is now within budget, still in the c3 honest-degradation class, or otherwise limited by the measured result. Keep the existing honest-limitations tone; do not claim a traffic-confidence, lane-guidance, or turn-restriction capability.
|
||||
|
||||
Update `docs/ALT-DIRECTION-SPLIT.md` with a closing header stating: `Status: SUPERSEDED by Phase 08.1 Contraction Hierarchies (2026-10-26)` and a short paragraph explaining that CH closes SC-1 at a lower proof burden because its optimality needs no new meet-rule proof. Preserve the entire measured-facts table and the one-directional-Lipschitz law below the header; the facts remain the historical record of why CH was chosen. Do not delete the existing analysis.
|
||||
|
||||
Also update the ALT roadmap-pointer row in `docs/KNOWN-LIMITATIONS.md` and any ALT phase pointer in `docs/BUDGETS.md` so no document tells the reader that ALT is the active engine or that the direction-split experiment is still open.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>grep -q 'SUPERSEDED' docs/ALT-DIRECTION-SPLIT.md && grep -q 'CH' docs/BUDGETS.md && grep -q 'CH' docs/KNOWN-LIMITATIONS.md && ! grep -qi 'direction-split potentials.*still open' docs/KNOWN-LIMITATIONS.md</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `docs/ALT-DIRECTION-SPLIT.md` contains `SUPERSEDED by Phase 08.1 Contraction Hierarchies` and still contains its original measured-facts table rows.
|
||||
- `docs/BUDGETS.md` contains the measured CH p95 value copied from Plan 02, the literal `8 ms`, and the words `CH` and `ARG3`.
|
||||
- `docs/BUDGETS.md` states either `RETIRED` or `REMAINS` for the c3 class, matching Plan 02's measured verdict.
|
||||
- `docs/KNOWN-LIMITATIONS.md` contains `CH` and no longer presents ALT as the active engine.
|
||||
- `grep -c 'Contraction Hierarchies' docs/ALT-DIRECTION-SPLIT.md` is at least 1.
|
||||
</acceptance_criteria>
|
||||
<done>The records carry the measured CH truth, the c3 verdict matches the gate output, and the direction-split experiment is closed without erasing its historical facts.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| Public fork contract -> external implementer | docs/ROAD-GRAPH.md is the interface a fork or Phase 11 browser decoder relies on; an inaccurate offset table produces silent misdecoding. |
|
||||
| Measurement output -> published engineering record | Numbers copied into docs are the evidence for SC-1 closure; paraphrase or stale values could retire the c3 class dishonestly. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-08.1-16 | Tampering | Public ARG3 header/section documentation | mitigate | The documented offsets are copied from `routing/src/graph.ts`, and acceptance criteria require the exact offsets 0, 4, 8, 12, 16, 20, 24, 56, 60, 64, 68 plus the full section order and the alignment law. |
|
||||
| T-08.1-17 | Repudiation | Published budget claim differing from the measured gate output | mitigate | The plan requires copying the exact printed p95, byte, and wall-clock values from Plan 02's campaign; acceptance criteria require the literal `8 ms` and an explicit RETIRED/REMAINS verdict. |
|
||||
| T-08.1-18 | Tampering | Erosion of the one-directional-Lipschitz historical facts | accept | D-06 intentionally preserves the historical measured facts; the risk is editorial, not technical. The closing header is additive and the existing analysis must remain intact. |
|
||||
| T-08.1-19 | Information Disclosure | Public docs exposing user location | accept | The documents contain only road-network geometry, build statistics, and public bbox-derived measurements; no user coordinates, chat ids, or route payloads are added. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
Manually read and compare the four record surfaces against the Plan 02 measurement output, then run:
|
||||
|
||||
```bash
|
||||
grep -q 'ARG3' docs/ROAD-GRAPH.md
|
||||
grep -q 'chShortcutCount' docs/ROAD-GRAPH.md
|
||||
grep -q 'SUPERSEDED' docs/ALT-DIRECTION-SPLIT.md
|
||||
grep -q 'CH' docs/BUDGETS.md
|
||||
grep -q 'CH' docs/KNOWN-LIMITATIONS.md
|
||||
npm run routing:test
|
||||
```
|
||||
|
||||
Expected: all greps succeed and the routing suite remains green.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- The ARG3 header and section contract is documented exactly and is browser-decodable by construction.
|
||||
- The build bytes, shortcut ratio, wall-clock, and both CPU p95 values in the docs match the measured Plan 02 output verbatim.
|
||||
- The c3 class record says RETIRED or REMAINS exactly as the measurement dictates.
|
||||
- The direction-split experiment is marked superseded with its measured facts intact.
|
||||
- No document claims ALT is the active engine or that a deployed-Worker CPU probe was performed.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/08.1-contraction-hierarchies-routing-backend-v3-the-lever-that-cl/08.1-03-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user