feat(08.1-01): rebuild all fixtures and bot graph builders through the ARG3 path
- mini-graph-arg2.bin: the frozen ARG2-era bytes preserved once as the D-12 decode-rejection vector (never regenerated — the arg1 precedent) - build-mini-graph.mjs: landmark stage out, CH stage in — contractGraph over the 25-node grid (41 shortcuts, 1.64/node, 0 budget hits) with fail-loud invariants: total-order rank, grid shortcut band, upward shortcuts, overlay-exact constituent weights (f64 replay, f32 compare), strong connectivity; deterministic ARG3 bytes - graph.test.mjs: committed-fixture G4 added (ARG3 magic/version, 25 nodes, 80 edges, rank 25, >=1 shortcut, ARG2 vector -> null); G7 restored (full ARG3 pin) - astar.test.mjs: A1-A6 restored — the 625-pair Dijkstra equivalence, oneway law, deriveSteps, depart/arrive, classifyTurn all green against the CH query on the committed ARG3 fixture - snap.test.mjs: S1-S6 restored on the ARG3 fixture; S3's synthetic graph rebuilt as the ARG3 ch-block star - webhook.test.mjs: landmarkBlock -> chBlock (contractGraph); corridor gains the fast nonstop chord (a pure line starves to zero shortcuts); sparse star keeps node ids 0-hub; RT9's split carrier is now two disconnected decodable stars (the honest A\* miss) - guidance-cron.test.mjs: GC6b's 2-node graph becomes the lowest-id-mid 3-node star (a 2-node graph cannot carry a valid shortcut) - routing 87 passed / bot 126 passed; routing+bot+web typechecks green
This commit is contained in:
+78
-12
@@ -31,6 +31,9 @@
|
||||
* G3 bad version -> null — ARG1 fixture bytes -> null (D-01 one-path
|
||||
* law); version 1, 2 and 4 tampers -> null
|
||||
* (the accepted version is 3)
|
||||
* G4 committed fixture — mini-graph.bin (ARG3) is 25 nodes / 80
|
||||
* edges / rank 25 / >= 1 shortcut; the frozen
|
||||
* mini-graph-arg2.bin decodes to null (D-12)
|
||||
* G4 truncated/hostile -> null — byte-length ≠ header-declared size
|
||||
* (incl. huge-count header on a tiny buffer,
|
||||
* T-06-03b: size check BEFORE view allocation)
|
||||
@@ -38,10 +41,12 @@
|
||||
* flipping an input byte flips the decoded view
|
||||
* G6 name pool — nameAt(0) === "Jalan Mini 1";
|
||||
* nameAt(UNNAMED_NAME_ID) === undefined
|
||||
* G7 committed fixture — SUSPENDED until task 3 regenerates
|
||||
* mini-graph.bin through the ARG3 encode path
|
||||
* (the committed bytes are still ARG2 here);
|
||||
* task 3 re-pins this case in place
|
||||
* G7 committed fixture — mini-graph.bin (ARG3, regenerated through
|
||||
* the ONE encode authority) decodes: 25 nodes,
|
||||
* 80 edges, 3 names, maxSpeed 60, exactly ONE
|
||||
* oneway flag, a total-order rank, >= 1
|
||||
* upward shortcut; the frozen ARG2 vector
|
||||
* decodes to null
|
||||
* G8 exported constants — GRAPH_MAGIC === 0x41524733, GRAPH_VERSION === 3
|
||||
* G9 table round-trip — the CH fields restore exactly; the CH arrays
|
||||
* are zero-copy views over the input buffer
|
||||
@@ -164,9 +169,12 @@ test("G2 bad magic -> null — a tampered header never half-decodes (T-06-03)",
|
||||
|
||||
test("G3 bad version -> null — ARG1 fixture bytes refuse; version 1, 2 and 4 tampers to null (D-01 one-path law)", async () => {
|
||||
const { encodeGraph, decodeGraph } = await loadGraph();
|
||||
// The frozen Plan 08-01 vector: REAL ARG1 bytes decode to null — the
|
||||
// The frozen rejection vectors: REAL ARG1 bytes decode to null — the
|
||||
// Phase 6 router-pending state covers every pre-ARG3 binary (D-01).
|
||||
// There is exactly one decode path; no v1/v2 fallback branch exists.
|
||||
// (mini-graph-arg2.bin — the frozen ARG2-era bytes, D-12 — is pinned in
|
||||
// the committed-fixture cases G4/G7; the version-2 tamper here covers
|
||||
// the same law in memory.)
|
||||
const arg1 = toArrayBuffer(readFileSync(path.join(here, "fixtures", "mini-graph-arg1.bin")));
|
||||
assert.equal(decodeGraph(arg1), null, "ARG1 bytes -> null, never a throw or a fallback decode");
|
||||
const buf = encodeGraph(syntheticInput());
|
||||
@@ -181,6 +189,26 @@ test("G3 bad version -> null — ARG1 fixture bytes refuse; version 1, 2 and 4 t
|
||||
assert.equal(decodeGraph(v4), null, "version 4 -> null (the accepted version is 3)");
|
||||
});
|
||||
|
||||
test("G4 committed ARG3 fixture — 25 nodes / 80 edges / rank 25 / >=1 shortcut; the preserved ARG2 vector decodes to null", async () => {
|
||||
const { decodeGraph, GRAPH_MAGIC, GRAPH_VERSION } = await loadGraph();
|
||||
// The committed ground truth, read as bytes: ARG3 header fields only.
|
||||
const bin = readFileSync(path.join(here, "fixtures", "mini-graph.bin"));
|
||||
assert.equal(bin.readUInt32LE(0), GRAPH_MAGIC, "the committed fixture is ARG3-magic");
|
||||
assert.equal(bin.readUInt32LE(4), GRAPH_VERSION, "the committed fixture is version 3");
|
||||
assert.equal(bin.readUInt32LE(8), 25, "25 nodes");
|
||||
assert.equal(bin.readUInt32LE(12), 80, "80 directed edges");
|
||||
assert.ok(bin.readUInt32LE(60) >= 1, "the committed hierarchy carries at least one shortcut");
|
||||
// Decoding sees the rank table's length too.
|
||||
const g = decodeGraph(toArrayBuffer(bin));
|
||||
assert.ok(g, "the committed fixture decodes as ARG3");
|
||||
assert.equal(g.chRank.length, 25, "one rank per node");
|
||||
assert.equal(g.chShortcutCount, bin.readUInt32LE(60), "the header count agrees with the decoded graph");
|
||||
// The preserved ARG2-era bytes (frozen before this fixture's
|
||||
// regeneration — D-12) decode to null: one decode path, no fallback.
|
||||
const arg2 = toArrayBuffer(readFileSync(path.join(here, "fixtures", "mini-graph-arg2.bin")));
|
||||
assert.equal(decodeGraph(arg2), null, "ARG2 bytes -> null (the D-07 rejection vector)");
|
||||
});
|
||||
|
||||
test("G4 truncated + hostile-size buffer -> null — exact byte-size check BEFORE any view allocation (T-06-03b)", async () => {
|
||||
const { encodeGraph, decodeGraph } = await loadGraph();
|
||||
const buf = encodeGraph(syntheticInput());
|
||||
@@ -227,13 +255,51 @@ test("G6 name pool — nameAt(0) resolves the first pool name; the unnamed senti
|
||||
assert.equal(g.nameAt(999), undefined, "out-of-range id -> undefined");
|
||||
});
|
||||
|
||||
test("G7 committed ARG3 fixture — SUSPENDED until task 3 regenerates mini-graph.bin through the ARG3 encode path", async () => {
|
||||
// The committed mini-graph.bin is still ARG2 at this point in the plan
|
||||
// (task 2 flips the codec; task 3 regenerates the fixture through the
|
||||
// ONE encode authority and re-pins this case in place, together with
|
||||
// the preserved mini-graph-arg2.bin rejection vector). Documented
|
||||
// no-op — the ONLY suspended case in this suite.
|
||||
assert.ok(true, "suspended: deferred to task 3 (fixture regeneration)");
|
||||
test("G7 committed ARG3 fixture — decodes to 25 nodes / 80 edges / 3 names / a real hierarchy, maxSpeed 60, ONE oneway", async () => {
|
||||
const { decodeGraph } = await loadGraph();
|
||||
const bin = toArrayBuffer(readFileSync(path.join(here, "fixtures", "mini-graph.bin")));
|
||||
assert.ok(bin.byteLength > 1000, `the committed fixture is substantial (${bin.byteLength} B)`);
|
||||
const g = decodeGraph(bin);
|
||||
assert.ok(g, "the committed fixture decodes cleanly (ARG3 — regenerated through the ONE encode authority, task 3)");
|
||||
assert.equal(g.nodeCount, 25, "5x5 grid");
|
||||
assert.equal(g.edgeCount, 80, "20 H + 20 V segments, both directions");
|
||||
assert.equal(g.nameCount, 3, "Jalan Mini 1 / Jalan Mini 2 / Jalan Persiaran Mini");
|
||||
assert.equal(g.nameAt(0), "Jalan Mini 1");
|
||||
assert.equal(g.nameAt(2), "Jalan Persiaran Mini");
|
||||
assert.equal(g.maxSpeedKmh, 60, "the perimeter speed class is the header max");
|
||||
// bbox of the hand-built grid (lat 3.1500–3.1520, lng 101.6900–101.6920)
|
||||
assert.deepEqual(g.bbox, { minLat: 3.15, minLng: 101.69, maxLat: 3.152, maxLng: 101.692 });
|
||||
let flagged = 0;
|
||||
for (let e = 0; e < g.edgeCount; e++) if ((g.edgeFlags[e] & 1) !== 0) flagged++;
|
||||
assert.equal(flagged, 1, "exactly ONE oneway-reverse edge in the whole fixture");
|
||||
// both speed classes present: perimeter ~55.6 m / 16.67 ≈ 3.3 s; interior ≈ 6.7 s
|
||||
let sawFast = false;
|
||||
let sawSlow = false;
|
||||
for (let e = 0; e < g.edgeCount; e++) {
|
||||
if (g.edgeSeconds[e] < 5) sawFast = true;
|
||||
if (g.edgeSeconds[e] > 5) sawSlow = true;
|
||||
}
|
||||
assert.ok(sawFast && sawSlow, "both speed classes (60 and 30 km/h) exist in the weights");
|
||||
// ARG3 CH laws (Phase 08.1, D-12): the fixture regenerates THROUGH the
|
||||
// encode authority with the tiny real contraction stage (S = 41 on the
|
||||
// 5x5 grid — the healthy ~1.6/node band).
|
||||
assert.equal(g.chRank.length, 25, "one rank per node");
|
||||
assert.equal(g.shortcutStart.length, 26, "CSR row pointers: nodeCount + 1");
|
||||
assert.equal(g.shortcutTarget.length, g.chShortcutCount, "shortcut CSR lengths agree with the header");
|
||||
assert.equal(g.shortcutSeconds.length, g.chShortcutCount, "shortcut seconds length agrees");
|
||||
assert.equal(g.shortcutVia.length, g.chShortcutCount, "shortcut via length agrees");
|
||||
assert.ok(g.chShortcutCount >= 1, "the committed hierarchy carries at least one shortcut");
|
||||
// Rank is a total order (Pitfall 9) and the shortcuts are upward.
|
||||
assert.equal(new Set(g.chRank).size, 25, "ranks are DISTINCT");
|
||||
for (let u = 0; u < g.nodeCount; u++) {
|
||||
for (let s = g.shortcutStart[u]; s < g.shortcutStart[u + 1]; s++) {
|
||||
const via = g.shortcutVia[s];
|
||||
assert.ok(g.chRank[u] > g.chRank[via] && g.chRank[g.shortcutTarget[s]] > g.chRank[via], `shortcut ${u}->${g.shortcutTarget[s]} is upward against via ${via}`);
|
||||
}
|
||||
}
|
||||
// The preserved ARG2-era bytes decode to null (D-12's rejection vector).
|
||||
const arg2 = toArrayBuffer(readFileSync(path.join(here, "fixtures", "mini-graph-arg2.bin")));
|
||||
assert.equal(decodeGraph(arg2), null, "ARG2 bytes -> null (the D-07 rejection vector)");
|
||||
});
|
||||
|
||||
test("G8 exported constants — GRAPH_MAGIC === 0x41524733 ('ARG3'), GRAPH_VERSION === 3", async () => {
|
||||
|
||||
Reference in New Issue
Block a user