Files
arah2/routing/scripts/measure-optimality-gate.mjs
T
araharah-executor 149636ce0a fix(08-05): D-03 optimality gate green — fromTable families removed from the ALT potential
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).
2026-10-25 19:54:24 +08:00

303 lines
14 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/*
* measure-optimality-gate.mjs — the D-03 optimality gate (Phase 8 plan 08-05).
* ------------------------------------------------------------------------------
* A MAINTAINER-TIME REPO TOOL — NOT a runtime dependency; nothing in bot/src,
* web/src, or routing/src imports it (the route-corpus/measure-route-cpu law).
*
* Purpose: prove the ALT preprocessing (ARG2 + landmark steering) did not move
* route OPTIMALITY, by re-routing the SAME coordinate-pinned pairs the frozen
* pre-ALT record (Plan 08-01's routing/test/fixtures/alt-baseline.json)
* measured, and gating on:
* D03A — seconds equality: |post − pre| within ± OPTIMALITY_EPSILON_PCT % of
* pre (equal seconds pass; equal-cost TIES are fine — D-03);
* D03B — verdict-class preservation: reachable/unreachable stays the same
* per pair (unsnappable pairs that now snap are an INFO count, not a
* violation — D-03's letter gates the reachable/unreachable classes).
*
* WHAT THE GATE NEVER DOES: compare NODE PATHS. A different equal-cost path
* (a tie) is documented behavior under bidirectional search + ALT potentials —
* structurally invisible to this gate (no path read exists here; only seconds
* and verdict classes are compared).
*
* Inputs:
* - the COMMITTED record routing/test/fixtures/alt-baseline.json (IMMUTABLE
* — the recorder refuses overwrite; delete by explicit git revert only);
* - the LOCAL rebuilt binary routing/data/kl-roadgraph.v1.bin (ARG2 — the
* storage NAME stays v1; the header version is the format truth).
*
* Constant-mirror law: this script's OPTIMALITY_EPSILON_PCT is asserted EQUAL
* to the record's epsilon block BEFORE any measurement — a record edit alone
* cannot fake a pass. Extract drift (node/edge count deltas vs the record) is
* a printed WARNING, never a violation, and NEVER a reason to re-record
* (Pitfall 1: the record is the anchor; mass failures escalate to the user).
*
* Echo law: pair ids + measurements ONLY (the route-corpus posture) — pair
* coordinates in the record are public bbox-derived values, never user data.
*
* Usage:
* node routing/scripts/measure-optimality-gate.mjs
*
* Exit codes: 0 GATE PASS (zero violations across both checks); 1 any
* violation, a mirror-assert failure, or an absent/undecodable input.
*/
import { existsSync, readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { decodeGraph } from "../src/graph.ts";
import { snapToGraph as snap } from "../src/snap.ts";
import { routeAstar as astar } from "../src/astar.ts";
const here = dirname(fileURLToPath(import.meta.url)); // routing/scripts
/** The frozen pre-ALT record — the D-03 anchor (committed, immutable). */
const RECORD_PATH = resolve(here, "..", "test", "fixtures", "alt-baseline.json");
/** The current binary under test (same path law as measure-route-cpu.mjs). */
const GRAPH_PATH = resolve(here, "..", "data", "kl-roadgraph.v1.bin");
/**
* D-03's named epsilon (±0.1% of the pre-ALT seconds) — the MIRROR of the
* record's epsilon block. The main() assert below refuses to measure when
* the two disagree: the record cannot be edited to fake an epsilon, and this
* constant cannot drift from the record silently.
*/
const OPTIMALITY_EPSILON_PCT = 0.1;
/** The record must be the PRE-ALT baseline (Pitfall 1's guard, mirrored). */
const REQUIRED_BASELINE_ARG_VERSION = 1;
function fail(message) {
console.error(`measure-optimality-gate: ${message}`);
process.exit(1);
}
function toArrayBuffer(buf) {
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}
/**
* The measurement core — exported so a suite can mirror the SAME numbers this
* CLI prints (one measurement, two reporters — the route-corpus split).
*
* For each record pair: snap both endpoints on the CURRENT graph, route with
* the CURRENT search, and classify:
* "pass" — seconds within epsilon (both routed) or the same null
* verdict class preserved (unreachable/unsnappable);
* "violation" — a D03A seconds break or a D03B reachable/unreachable class
* break;
* "re-snapped" — record-unsnappable that now snaps (INFO; counted, printed
* as a WARNING in the summary when > 0, never a violation);
* "unsnapped" — a record-routed pair whose snap now misses (a D03B class
* break — the pair no longer routes at all).
*
* @param graph decoded RoadGraph (the current ARG2 build)
* @param record parsed alt-baseline.json (the frozen pre-ALT record)
* @param opts { snap, astar } — the live modules (injected like the corpus core)
* @returns {{ checks: { D03A: check, D03B: check }, rows: Array, reSnapped: number, pass: boolean }}
*/
export function measureOptimality(graph, record, { snap, astar }) {
const checks = {
D03A: { id: "D03A", label: "seconds equality (±0.1%)", violations: [] },
D03B: { id: "D03B", label: "verdict class preserved", violations: [] },
};
const rows = [];
let reSnapped = 0;
for (const pair of record.pairs) {
const pre = pair.seconds; // number | null
const verdictPre = pair.verdict; // "reachable" | "unreachable" | "unsnappable"
const origin = snap(graph, pair.origin);
const goal = snap(graph, pair.destination);
let post = null;
let verdictPost = "unsnappable";
if (origin !== null && goal !== null) {
const result = astar(graph, origin.node, goal.node);
if (result === null) verdictPost = "unreachable";
else {
verdictPost = "reachable";
post = result.seconds;
}
}
let cls = "pass";
let deltaPct = null;
if (verdictPre === "unsnappable") {
if (verdictPost === "unsnappable") {
cls = "pass"; // unsnappable preserved — the honest null, stable
} else {
// The pair now snaps (+ routes or honestly reports unreachable):
// an INFO re-snap, counted and summary-WARNED, NOT a violation.
cls = "re-snapped";
reSnapped += 1;
}
} else if (verdictPost === "unsnappable") {
cls = "unsnapped";
checks.D03B.violations.push({
pairId: pair.pairId,
reason: `verdict class break: ${verdictPre} (pre-ALT record) -> unsnappable (post-ALT snap miss)`,
});
} else if (verdictPre !== verdictPost) {
cls = "violation";
checks.D03B.violations.push({
pairId: pair.pairId,
reason: `verdict class break: ${verdictPre} -> ${verdictPost}`,
});
} else if (verdictPost === "reachable") {
// Both sides routed — D03A seconds equality (paths are NEVER compared;
// equal-cost tie differences cannot produce a violation by construction).
deltaPct = ((post - pre) / pre) * 100;
if (Math.abs(deltaPct) > OPTIMALITY_EPSILON_PCT) {
cls = "violation";
checks.D03A.violations.push({
pairId: pair.pairId,
reason: `seconds drift ${pre.toFixed(1)}s -> ${post.toFixed(1)}s (Δ ${deltaPct.toFixed(3)}% beyond ±${OPTIMALITY_EPSILON_PCT}%)`,
});
}
}
// else: unreachable -> unreachable — class preserved, pass.
rows.push({ pairId: pair.pairId, pre, post, deltaPct, verdictPre, verdictPost, class: cls });
}
const pass =
checks.D03A.violations.length === 0 && checks.D03B.violations.length === 0;
return { checks, rows, reSnapped, pass };
}
/* ————— CLI reporter ————— */
function formatSeconds(v) {
return v === null ? "unsnappable" : `${v.toFixed(1)}s`;
}
function report(record, results, drift) {
const routed = results.rows.filter((r) => r.class === "pass" && r.pre !== null && r.post !== null);
const unreachPreserved = results.rows.filter(
(r) => r.verdictPre === "unreachable" && r.verdictPost === "unreachable",
).length;
const unsnapPreserved = results.rows.filter(
(r) => r.verdictPre === "unsnappable" && r.verdictPost === "unsnappable",
).length;
const maxAbsDelta = routed.reduce((m, r) => Math.max(m, Math.abs(r.deltaPct ?? 0)), 0);
console.log(`D-03 optimality gate — ${results.rows.length} record pairs vs the current binary`);
for (const row of results.rows) {
const delta = row.deltaPct === null ? "Δ —" : `Δ ${row.deltaPct >= 0 ? "+" : ""}${row.deltaPct.toFixed(3)}%`;
const tag =
row.class === "re-snapped"
? "re-snapped INFO"
: row.class === "unsnapped" || row.class === "violation"
? "FAIL"
: "pass";
const note = row.class === "re-snapped" ? " (re-snapped — no pre-ALT seconds to compare)" : "";
console.log(` ${row.pairId}: pre ${formatSeconds(row.pre)} post ${formatSeconds(row.post)} (${delta})${note} ${tag}`);
}
console.log(
`summary: ${results.rows.length} record pairs — ${routed.length} routed equal, ` +
`${unreachPreserved} unreachable preserved, ${unsnapPreserved} unsnappable preserved, ` +
`${results.reSnapped} re-snapped`,
);
for (const check of Object.values(results.checks)) {
console.log(` ${check.id} (${check.label}): ${check.violations.length} violation(s)`);
for (const v of check.violations) {
console.log(` ${v.pairId}: ${v.reason}`);
}
}
console.log(` max |Δ%| over routed pairs: ${maxAbsDelta.toFixed(3)}%`);
console.log(
` epsilon: ±${OPTIMALITY_EPSILON_PCT}% (OPTIMALITY_EPSILON_PCT — mirrored to the record's epsilon block: ${record.epsilon.pct}%)`,
);
if (drift !== null) {
console.log(
` extract drift vs the frozen record: nodes ${drift.nodes >= 0 ? "+" : ""}${drift.nodes}, edges ${drift.edges >= 0 ? "+" : ""}${drift.edges} — the seconds gate still applies`,
);
}
if (results.reSnapped > 0) {
console.log(
`WARNING: ${results.reSnapped} record-unsnappable pair(s) now snap on the current graph (re-snapped INFO rows above) — counted, not a D-03 violation`,
);
}
if (!results.pass) {
const a = results.checks.D03A.violations.length;
const b = results.checks.D03B.violations.length;
console.error(
`GATE FAIL: D-03 optimality — ${a} D03A + ${b} D03B violations across ${results.rows.length} record pairs. ` +
`The record is IMMUTABLE (Pitfall 1): suspect extract drift first (the WARNING counts above), then escalate to the user — never edit or re-record the baseline.`,
);
return false;
}
console.log(
`GATE PASS: D-03 optimality — ${routed.length}/${routed.length} routed pairs equal within ±${OPTIMALITY_EPSILON_PCT}%, verdict classes preserved ` +
`(${results.rows.length}/${results.rows.length} record pairs; ${unreachPreserved} unreachable + ${unsnapPreserved} unsnappable preserved, ${results.reSnapped} re-snapped)`,
);
return true;
}
function main() {
// Honest-refusal structure: EVERY guard below precedes any measurement call.
if (!existsSync(RECORD_PATH)) {
fail(`no baseline record at ${RECORD_PATH} — run measure-optimality-baseline first — the record is the D-03 anchor`);
}
if (!existsSync(GRAPH_PATH)) {
fail(`no graph at ${GRAPH_PATH} — build it first: node routing/scripts/build-road-graph.mjs`);
}
const record = JSON.parse(readFileSync(RECORD_PATH, "utf8"));
if (!record || typeof record !== "object" || !Array.isArray(record.pairs) || record.pairs.length === 0) {
fail(`${RECORD_PATH} has no pairs array — the record schema is the gate's input contract`);
}
if (!record.graph || typeof record.graph.argVersion !== "number") {
fail(`${RECORD_PATH} has no graph block — the record schema is the gate's input contract`);
}
// CONSTANT-MIRROR ASSERT (must precede any measurement): the gate's epsilon
// equals the record's epsilon block, by name and by value — a record edit
// alone cannot fake a pass.
if (record.epsilon?.name !== "OPTIMALITY_EPSILON_PCT" || record.epsilon?.pct !== OPTIMALITY_EPSILON_PCT) {
fail(
`epsilon mirror mismatch: the gate's OPTIMALITY_EPSILON_PCT is ${OPTIMALITY_EPSILON_PCT} but the record's epsilon block says ` +
`${JSON.stringify(record.epsilon ?? null)} — align the constant with the record's provenance (or git-revert the record edit); the record itself is immutable`,
);
}
if (record.graph.argVersion !== REQUIRED_BASELINE_ARG_VERSION) {
fail(
`the baseline must be the pre-ALT record (argVersion ${REQUIRED_BASELINE_ARG_VERSION}) — this record says argVersion ${record.graph.argVersion}`,
);
}
const graph = decodeGraph(toArrayBuffer(readFileSync(GRAPH_PATH)));
if (graph === null) {
fail(
`${GRAPH_PATH} does not decode (fail-honest null — still ARG1 or corrupt) — rebuild the ARG2 binary: node routing/scripts/build-road-graph.mjs`,
);
}
console.log(
`record: ${record.pairs.length} pairs recorded ${record.recordedAt} ` +
`(ARG${record.graph.argVersion}: ${record.graph.nodeCount} nodes / ${record.graph.edgeCount} edges, epsilon ±${record.epsilon.pct}%)`,
);
console.log(`current: ${graph.nodeCount} nodes / ${graph.edgeCount} edges (decoded from ${GRAPH_PATH})`);
let drift = null;
const nodeDrift = graph.nodeCount - record.graph.nodeCount;
const edgeDrift = graph.edgeCount - record.graph.edgeCount;
if (nodeDrift !== 0 || edgeDrift !== 0) {
drift = { nodes: nodeDrift, edges: edgeDrift };
console.log(
`WARNING: extract drift vs the frozen record — the seconds gate still applies (NEVER a reason to re-record; Pitfall 1)`,
);
}
const results = measureOptimality(graph, record, { snap, astar });
const pass = report(record, results, drift);
process.exit(pass ? 0 : 1);
}
// Run only when invoked directly — importing this module (an in-process
// mirror of the same numbers) must never execute the CLI (route-corpus law).
const invoked = process.argv[1] ? pathToFileURL(resolve(process.argv[1])).href : "";
if (invoked === import.meta.url) {
main();
}