189 lines
19 KiB
Markdown
189 lines
19 KiB
Markdown
---
|
||
phase: 08-alt-landmark-preprocessing-routing-backend-v2
|
||
plan: 05
|
||
subsystem: routing
|
||
tags: [optimality-gate, d03, cpu-campaign, c3-escalation, one-directional-lipschitz, honest-fail, budget-gates, docs-record]
|
||
|
||
requires:
|
||
- phase: 08-alt-landmark-preprocessing-routing-backend-v2 (plan 08-01)
|
||
provides: the frozen pre-ALT record (routing/test/fixtures/alt-baseline.json) — the D-03 anchor
|
||
- phase: 08-alt-landmark-preprocessing-routing-backend-v2 (plans 08-02/03/04)
|
||
provides: the ARG2 binary + landmark module + ALT steering the campaign measures
|
||
- phase: 07-hardening-private-launch
|
||
provides: the budget-audit gate law + vectors this plan re-anchors
|
||
|
||
provides:
|
||
- the D-03 optimality gate (routing/scripts/measure-optimality-gate.mjs) — committed, re-runnable, GATE PASS vs the frozen record
|
||
- the measured post-ALT verdict (the phase's SC-1 answer): 25-route p95 152.788 ms / extended 100-pair p95 185.692 ms — honest GATE FAIL, quoted into every record
|
||
- the one-directional-Lipschitz law: fromTable triangle bounds cannot steer the shared-Ikeda bidirectional search soundly; toTable-only is the sound (and slower) configuration; the A11/A12 pins enforce it structurally
|
||
- the re-anchored route-cpu gate law (drift guard 200 → 400, the post-ALT band documented) — 19/19 audit vectors green
|
||
- the honest post-ALT records in KNOWN-LIMITATIONS, ROAD-GRAPH, BUDGETS — the c3 class REMAINS with quoted evidence
|
||
affects: [the maintainer escalation decision (direction-split potentials / CH / Workers Paid), future Malaysia-fork builds (the ARG2 layout + quantum knob are documented), Phase 11 Engine B (consumes the same ARG2 asset)]
|
||
|
||
tech-stack:
|
||
added: []
|
||
patterns:
|
||
- "the constant-mirror gate: a script's epsilon is asserted EQUAL to the record's epsilon block before any measurement — a record edit alone cannot fake a pass"
|
||
- "selective-hunk staging (git apply --cached) to keep task commits atomic on a tree carrying pre-existing doc-sync edits"
|
||
- "seconds-and-verdicts-only optimality gating: node paths are structurally never compared (equal-cost ties cannot fail the gate)"
|
||
|
||
key-files:
|
||
created:
|
||
- routing/scripts/measure-optimality-gate.mjs
|
||
modified:
|
||
- routing/src/astar.ts (the fromTable families removed from the potential — the D-03 gate's Rule-1 catch)
|
||
- routing/test/astar.test.mjs (A11 re-pinned + A12 oneway sweep)
|
||
- scripts/lib/budget-gates.mjs (the post-ALT verdict law, drift guard 400)
|
||
- scripts/test/budget-audit.test.mjs (CPU3 pins the executed-ALT truth)
|
||
- docs/BUDGETS.md (route-cpu row + c3 section = the measured post-ALT record)
|
||
- docs/KNOWN-LIMITATIONS.md (the cross-metro section = the /cari-shaped record; ALT row flipped)
|
||
- docs/ROAD-GRAPH.md ((c2) ladder rung, the ARG2 layout section, key facts, fork-contract build step)
|
||
|
||
key-decisions:
|
||
- "Rule 1: the D-03 gate's first run caught routeAstar returning SUBOPTIMAL real-graph routes (x038: 1234.6 s vs the 1214.8 s Dijkstra optimum; x045: 1475.5 vs 1460.6) — root-caused to the fromTable families being one-directional Lipschitz (the u-side term needs the reverse edge; a oneway does not provide one) inside the shared Ikeda average; the fix keeps only the two-sided toTable families — the stop rule, reductions, meet probes, and reconstruction are untouched"
|
||
- "The pre-committed ladder was executed to its documented stop: the L=32 rebuild measured WORSE (182.725/204.499 ms vs 152.788/185.692) — stopped there; tasks 3–4 re-scoped to honest escalation records per the plan's own branch (retirement never fires on failing numbers)"
|
||
- "Escalation over silent acceptance: the deeper theoretical finding (no two-sided landmark extension exists on a graph with sentinel fringes — a finite filler would chain via real edges into a finite path to the landmark) means sound+fast requires direction-split potentials with a re-proven meet rule — search-skeleton work that Phase 8's own untouchable lines forbid; this is the maintainer decision the SUMMARY hands over"
|
||
|
||
duration: ~50min
|
||
completed: 2026-10-25
|
||
---
|
||
|
||
# Phase 8 Plan 08-05: Measurement Campaign & Record Truth Summary
|
||
|
||
**The D-03 optimality gate is committed and GREEN (53/53 routed pairs equal the frozen pre-ALT record within ±0.1%) — but only after it caught the four-family ALT steering returning suboptimal real-graph routes; the sound configuration's CPU campaign measured HONEST GATE FAILs (25-route p95 152.788 ms, extended 185.692 ms vs the 8 ms budget; the pre-committed L=32 ladder rung measured worse), so the c3 class is NOT retired — every record (budget gate law, BUDGETS, KNOWN-LIMITATIONS, ROAD-GRAPH) now carries the quoted post-ALT truth and the escalation is handed to the maintainer.**
|
||
|
||
## ⚠️ Escalation to the maintainer (read this first)
|
||
|
||
The phase's success criteria land as: **SC-2 (optimality) PASS — SC-1 (CPU within budget) FAIL — SC-3 (c3 retirement) NOT TAKEN (the evidence forbids it).** Three gates decide, and the decision inputs are:
|
||
|
||
```
|
||
GATE PASS: D-03 optimality — 53/53 routed pairs equal within ±0.1%, verdict classes preserved (100/100 record pairs; 6 unreachable + 41 unsnappable preserved, 0 re-snapped)
|
||
GATE FAIL: p95 152.788 ms exceeds the 8 ms budget (A1) [25-route CI gate, n=23, L=24, 2026-10-25]
|
||
GATE FAIL: p95 185.692 ms exceeds the 8 ms budget — D-04 extended verdict [100 cross-metro pairs, n=53, L=24, 2026-10-25]
|
||
GATE FAIL: p95 182.725 ms / 204.499 ms [the ladder's L=32 probe — worse, stopped per plan]
|
||
```
|
||
|
||
The options on the table (all documented in the records): (1) direction-split potentials with a re-proven bidirectional meet rule — search-skeleton work Phase 8's own laws forbid; (2) Contraction Hierarchies (the Phase-6 deferral); (3) Workers Paid — rejected by the $0 law, reversal needs sign-off; (4) keep c3 as the designed degradation (the current shipped posture — router-pending on long reroutes).
|
||
|
||
## Performance
|
||
|
||
- **Duration:** ~50 min (11:19–12:09 UTC)
|
||
- **Started:** 2026-10-25T11:19:42Z
|
||
- **Completed:** 2026-10-25T12:09:32Z
|
||
- **Tasks:** 4/4 complete (tasks 3–4 re-scoped per the plan's own honest-FAIL branch)
|
||
- **Files modified:** 7
|
||
|
||
## Accomplishments
|
||
|
||
- **The D-03 gate is a machine gate and it is GREEN.** `routing/scripts/measure-optimality-gate.mjs` (route-corpus architecture: exported `measureOptimality` core + CLI guard) mirrors the record's epsilon (`OPTIMALITY_EPSILON_PCT = 0.1` — asserted equal to `record.epsilon` by name AND value, before any measurement), refuses non-ARG1 records, refuses an undecodable binary with the rebuild hint, prints the extract-drift WARNING (+36 nodes/+58 edges — never a re-record), compares SECONDS AND VERDICT CLASSES ONLY (no path reads exist — `nodes[0]`/`nodes.length` grep = 0), and exits honestly. Final run on the shipped L=24 binary: **GATE PASS**, max |Δ| 0.005%.
|
||
- **The gate caught a real optimality bug the fixture sweep could not see.** First run: 2 D03A violations (x038 +1.637%, x045 +1.019%). A throwaway clean forward Dijkstra on the same graph returned EXACTLY the record's seconds (1214.8 s / 1460.6 s) — the searches disagreed, not the graph. The landmark tables were audited EXACT (0 mismatches vs independent integer Dijkstras, both rows, all 24 anchors — after fixing my own audit's gap-phantom bug). Root cause found by instrumenting labels along the optimal path: the four-family potential's fromTable bound `d̂(ℓ,t) − d̂(ℓ,v)` jumps +126.8 s across a 1.49 s edge (the anchor reaches 264611 126.8 s faster than its own upstream 264610 — a oneway reach asymmetry), so the shared Ikeda average `p = (h_f − h_b)/2` had reduced costs as low as −62.6 s on unexpanded edges, and BH's closed-set stop rule closed 264611 with a label 43.1 s worse than optimal. Fixtures are bidirectional — no reach asymmetry — which is why 625-pair A1 passed.
|
||
- **The fix is the sound subset, structurally pinned.** `potential()` now consults ONLY the toTable families (+ haversine) — both two-sided-Lipschitz (u→v→ℓ and v→u→ℓ ride stored traversable edges). The stop rule, reductions, meet probes, and reconstruction are byte-untouched. A11 now pins that `landmarkFrom` must NEVER appear in the comment-stripped search; a new A12 oneway-ring fixture (81-pair Dijkstra sweep with one-way-in spur sentinels) guards the law. routing 80/80, bot 126/126, both typechecks green.
|
||
- **The CPU campaign ran both verdict modes and the pre-committed ladder, honestly.** L=24: 25-route `GATE FAIL: p95 152.788 ms` / extended `GATE FAIL: p95 185.692 ms`. Ladder rung (a): `--landmarks 32` rebuild (52.7 MB, coverage 0.980) re-measured WORSE (182.725 / 204.499) — stopped at the plan's documented stop; nothing was retuned, reseeded, or re-rolled (`P95_BUDGET_MS = 8` grep = 1).
|
||
- **The sound configuration's weakness is theoretical, not incidental:** no two-sided-Lipschitz landmark extension exists on a graph with sentinel fringes (a finite filler at a sentinel node would chain through real edges into a finite path to the landmark — contradiction). The only fully sound potentials for the shared-average BH are haversine-class. Sound+fast requires direction-split potentials (`π_f` toward goal from toTable, `π_r` toward origin from fromTable) with a re-proven meet rule — skeleton work outside this phase's untouchable lines. This is recorded, not papered over.
|
||
- **Tasks 3–4 executed as the plan's honest-FAIL branch prescribes:** the budget gate law now documents the executed-and-measured ALT verdict (no "next-milestone item" wording survives anywhere — grep = 0; drift guard re-anchored 200 → 400 because the documented post-ALT band outgrew the old one; the 8 ms budget unchanged and its FAIL still prints); audit 19/19 vectors ok. KNOWN-LIMITATIONS carries the /cari-shaped record (WHAT CHANGED / what REMAINS, quoted gate lines); its ALT roadmap row reads "Built (Phase 8, 2026-10-25): optimality verified; the CPU verdict measured over budget". ROAD-GRAPH's ladder table carries the (c2) rung with the measured FAIL, the decision paragraph names the real remaining options, and the new "The ARG2 binary layout" section teaches the 72-byte header, the u16 suffix, the sentinel law, and the `landmarkQuantumS` fork knob (≈ 6,553 s ceiling, raise-at-rebuild) plus the storage-name-vs-format-truth law.
|
||
- **docs/CONFIGURATION.md: verify-only, no-op** — every path/script claim checked (lines 24, 247–248, 294–297): still true; nothing claimed ARG1 decodeable.
|
||
|
||
## Task Commits
|
||
|
||
1. **task 1: the D-03 gate + the Rule-1 optimality fix it forced** — `149636c` (fix)
|
||
2. **task 2: the CPU verdict campaign (no commit — measurement only; both modes + the L=32 ladder probe, quoted above)**
|
||
3. **task 3: the route-cpu gate law re-anchored (escalation record)** — `79a302c` (fix)
|
||
4. **task 4: the docs records (escalation, no retirement)** — `720ad43` (docs)
|
||
|
||
## Deviations from Plan
|
||
|
||
### Auto-fixed Issues
|
||
|
||
**1. [Rule 1 - Bug] The four-family ALT potential returned suboptimal real-graph routes**
|
||
- **Found during:** task 1 (the gate's first real run — its purpose)
|
||
- **Issue:** fromTable bounds are one-directional Lipschitz across oneway-adjacent edges; the shared `p = (h_f − h_b)/2` needs two-sided families; BH closed nodes suboptimally (x038/x045 above)
|
||
- **Fix:** potential() consults `landmarkTo` only; A11/A12 pins enforce it
|
||
- **Files modified:** routing/src/astar.ts, routing/test/astar.test.mjs
|
||
- **Commit:** `149636c`
|
||
|
||
### Honest-Stop (the plan's pre-committed branch)
|
||
|
||
**2. The CPU verdict FAILED; the ladder ran to its stop; tasks 3–4 re-scoped to escalation records.** The plan wrote this branch itself ("at L=32 still FAIL → STOP … tasks 3–4 re-scope to an honest escalation record instead"). No budget constant was touched, no sample dropped, no lucky re-roll.
|
||
|
||
**3. The plan's task-1 stop-rule was superseded by a better diagnosis:** the plan said a D-03 FAIL means "suspect extract drift first … escalation to the user is the only next step". Extract drift was DISPROVEN by measurement (the Dijkstra optimum on the current graph equals the record's seconds exactly), which converted the FAIL from a data problem into a code bug — Rule 1 — fixable within the sanctioned surface. The deviation rules took precedence; the record was never touched.
|
||
|
||
**Total deviations:** 1 auto-fixed bug + 1 pre-committed honest stop. **Impact:** SC-2 green; SC-1 honestly FAIL with quoted numbers; SC-3 correctly NOT taken. The binary on disk is the canonical L=24 ARG2 build (D-02 rule; D-03 green); the R2 upload remains the maintainer's deploy-time act.
|
||
|
||
## Known Stubs
|
||
|
||
None. Every number quoted in this summary and in the records comes from a printed gate line in this session (files under /tmp/opencode/: d03-final.txt, cpu-25-final.txt, cpu-ext-final.txt, build-l24-restore.txt, build-l32.txt, cpu-25-L32.txt, cpu-ext-L32.txt, d03-gate-L32.txt).
|
||
|
||
## Threat Surface Scan
|
||
|
||
No new network endpoints, auth paths, file access patterns, or schema changes — the gate script reads two local files and prints; the T-08-13/14/15 mitigations (epsilon mirror, quoted-verbatim records, drift WARNING + immutable record) are all implemented and exercised.
|
||
|
||
## Verification Record (as-run)
|
||
|
||
- `node routing/scripts/measure-optimality-gate.mjs` → exit 0, `GATE PASS: D-03 optimality — 53/53 routed pairs equal within ±0.1%, verdict classes preserved` (final, L=24)
|
||
- L=32 probe: the same gate exit 1 with x045 +0.761% (the fringe-boundary finding — documented, not shipped)
|
||
- `node routing/scripts/measure-route-cpu.mjs` → exit 1, `GATE FAIL: p95 152.788 ms exceeds the 8 ms budget (A1)`
|
||
- `node routing/scripts/measure-route-cpu.mjs --extended` → exit 1, `GATE FAIL: p95 185.692 ms exceeds the 8 ms budget — D-04 extended verdict`
|
||
- `--landmarks 32` rebuild → exit 0 (52.7 MB, L=32, coverage 0.980) → re-measured 182.725 / 204.499 ms — worse; ladder stopped
|
||
- `npm run routing:test` → 80 passed, 0 failed (A1 sweep + A11 pin + A12 oneway sweep)
|
||
- `npm test -w bot` → 126 passed, 0 failed; `routing:typecheck` + `bot:typecheck` clean
|
||
- `node scripts/test/budget-audit.test.mjs` → `BUDGET-AUDIT SUITE: 19/19 vectors ok`
|
||
- `grep -c "P95_BUDGET_MS = 8" routing/scripts/measure-route-cpu.mjs` = 1; `grep -c "next-milestone" scripts/lib/budget-gates.mjs docs/BUDGETS.md` = 0+0; `nodes[0]|nodes.length` in the gate = 0
|
||
- The record file: untouched (verified by the gate's own mirror asserts + git)
|
||
|
||
## F1 Remediation Addendum (post-plan, 2026-10-25)
|
||
|
||
**Debug session:** `.planning/debug/alt-potential-4x-slowdown.md` — RC-1 (LIVE correctness bug) + RC-2 (the 4× CPU slowdown). D-03's green above was sampling luck: the fixed corpus never crosses a violating edge harmfully, while fresh random pairs caught the shipped `routeAstar` suboptimal on **1/194** (+0.278%, pair 141683→292552) vs a Dijkstra oracle. Root cause: `bound()`'s per-node sentinel skip varied the active landmark family set PER NODE across edges leaving the 6,285-node disconnected island (2.02% of cells).
|
||
|
||
**Fix (F1, exactly as the debug report specified):** `routing/src/astar.ts`
|
||
- Per-graph WeakMap memo `cleanColumnsFor` — `clean[l]` = "to-column ℓ has zero 0xFFFF cells" (O(L·n) sequential rows once per graph/isolate; `reverseFor` precedent).
|
||
- Per-search active list `activeL` = clean columns only; `bound()` loops the active list with NO per-node sentinel branch (constant-table goal/origin NaN checks retained as belt).
|
||
- On the current binary (0/24 clean columns — island census confirmed) potentials degrade to EXACTLY haversine: the designed sound fallback; the pre-ALT CPU band returns.
|
||
- ALT stays LIVE and tested: the committed mini-graph is 0/2 dirty columns and A12's ring is all-clean (both verified by table scan), so the ALT path keeps running in the suite.
|
||
- BH stop rule, reductions, balanced alternation, meet probes, real-seconds reconstruction: untouched. F2 (transposed table) explicitly out of scope — future clean-column binaries only.
|
||
|
||
**Test work (re-specified per the debug report):**
|
||
- A9: island fixture re-pinned as ALL-DIRTY (every to-column holds a B-node sentinel → the whole fixture runs haversine); the old "real ALT bounds live in A" claim (per-node-skip semantics) removed.
|
||
- A10: mechanism comment updated to the containment law.
|
||
- A11: structural pin rewritten to the F1 shape — `cleanColumnsFor`/`activeL` must exist in code, inline `=== 0xffff` sentinel compares BANNED (RC-1's exact shape), named-constant law pinned. Committed RED-first: failed on shipped code exactly as designed.
|
||
- A12: re-sealed as the ALT-ACTIVE counterpart (all to-columns clean, pinned by table scan).
|
||
- A13 (new): mixed clean/dirty columns via a one-way-bottleneck twin-ring fixture — the subset-active-list branch; 64-pair Dijkstra equivalence + 16 honest nulls.
|
||
|
||
**New committed gate:** `routing/scripts/measure-astar-oracle-sweep.mjs` — 200 COMPARED fresh seeded cross-metro pairs (seed `0x08f1`, NOT the D-03 corpus; `generateCrossMetroPairs` core reused; 3× oversample with snap-miss rejection printed; exported `sweepOracle` core + CLI guard; honest SKIP when the gitignored binary is absent). Gate: 0 suboptimal vs a brute-force label-setting Dijkstra oracle.
|
||
|
||
**Verification (all re-run post-fix, this host):**
|
||
|
||
| Gate | Result |
|
||
| ---- | ------ |
|
||
| `npm run routing:test` | 81 passed, 0 failed (A11 RED pins green, A13 new) |
|
||
| `npm test -w bot` | 126 passed, 0 failed |
|
||
| `routing:typecheck` + `bot:typecheck` | clean |
|
||
| Oracle sweep (new) | GATE PASS: 0/200 suboptimal (182 routed, 18 unreachable, worst Δ +0.000%) |
|
||
| `measure-route-cpu.mjs` | p95 136.562 ms / mean 54.081 ms — the pre-ALT band on this host (pre-fix today measured 1037.618 ms; ~7.6× back). Honest c3 record stands: still a FAIL vs the 8 ms budget |
|
||
| `measure-optimality-gate.mjs` (D-03) | GATE PASS: 0 violations, max \|Δ\| 0.005%, verdict classes preserved |
|
||
|
||
**Commits:** `c071e20` (test RED), `2a5a0b5` (fix F1), `97d6e35` (oracle sweep). Bench/instrumentation harnesses remain in `/tmp/opencode/alt-bench/` (never committed).
|
||
|
||
**Impact on the plan's verdicts:** the D-03 gate green above is now backed by a fresh-pair pin that structurally can't pass on sampling luck; the CPU verdict is unchanged in kind (c3 escalation stands — ALT is inert on this binary until a clean-column rebuild exists, and a rebuilt clean-column binary re-arms ALT with no code change).
|
||
|
||
## User Setup Required
|
||
|
||
None new. The maintainer-time R2 upload (`node routing/scripts/upload-road-graph.mjs` + the `--check` roundtrip) remains the deploy-time act — manual by design (account-level validation). **Decision needed:** pick the escalation track (direction-split potentials / CH / Workers Paid reversal / keep c3).
|
||
|
||
## Next Phase Readiness
|
||
|
||
- The ARG2 asset is built, checked, and upload-ready; Engine B (Phase 11) consumes the same layout (the ~43 MB payload class holds at L=24).
|
||
- The optimality guarantee now travels with a machine gate any future steering change must keep green.
|
||
- The c3 record (BUDGETS §c3 + KNOWN-LIMITATIONS) is the single source of truth for the escalation options.
|
||
|
||
---
|
||
*Phase: 08-alt-landmark-preprocessing-routing-backend-v2*
|
||
*Completed: 2026-10-25*
|
||
|
||
## Self-Check: PASSED
|
||
|
||
- All 8 touched files + the SUMMARY exist on disk — FOUND (9/9)
|
||
- Task commits `149636c`, `79a302c`, `720ad43` exist in git history — FOUND (3/3)
|
||
- Task 2 is intentionally commit-free (measurement only; the quoted gate lines are the deliverable) — VERIFIED
|
||
- The record `routing/test/fixtures/alt-baseline.json` is untouched — VERIFIED (git status; the gate's mirror asserts ran on every pass)
|
||
|