/** * rigor/complex-graph-quantization-crucible.test.js — condensed-snapshot * quantization parity over the community graph. * * The generator emits varied non-dyadic possibilities (0.3..1.0). The * query set is drawn from the graph's OWN edges (direct_access, member-fed * TTU can_read, delegate-fed chain can_delegate_read) so the sampled * queries are granted and actually carry quantization error — random * (user, resource) pairs are mostly denied (live == restored == 0) and * would make the band vacuous. * * QUANT-BAND — |live - restored| <= 2 * QUANT_STEP (the chain path * multiplies two quantized inputs, so twice the single * value's band). * DECISION — outside the quantization band of the threshold the * allow/deny decision must agree; inside it a flip is * allowed (the existing snapshot-quantization-parity rule). * RE-SERIALIZE— snapshot-of-snapshot (serialize the restored engine, * restore again) is semantically identical. * * Deterministic fixed loop over seeds — no campaign needed. */ import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { Arbiter } from '../../src/index.js'; import { makeCommunityGraph } from './complex-graphs.js'; const QUANT_STEP = 0.5 / 65535; const TOL = QUANT_STEP * 2; const SAMPLES = 40; function mulberry32(seed) { let a = seed >>> 0; return function () { a |= 0; a = (a + 0x6d2b79f5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; } function grantedQuerySet(g) { const qs = []; const ownsBySrc = new Map(); for (const r of g.relations) if (r.rel === 'owns') ownsBySrc.set(r.src, r.dst); for (const r of g.relations) { if (r.rel === 'direct_access') qs.push([r.src, 'direct_access', r.dst]); if (r.rel === 'member') { const obj = ownsBySrc.get(r.dst); if (obj) { qs.push([r.src, 'can_read', obj]); qs.push([r.src, 'can_read_not_blocked', obj]); } } if (r.rel === 'delegate') { for (const m of g.relations) { if (m.rel === 'member' && m.dst === r.src) { qs.push([m.src, 'can_delegate_read', r.dst]); break; } } } } return qs; } function sample(querySet, seed) { const rng = mulberry32(seed * 997); const out = []; for (let i = 0; i < SAMPLES; i++) { out.push(querySet[Math.floor(rng() * querySet.length)]); } return out; } describe('Complex-graph snapshot quantization crucible', () => { it('QUANT-BAND + DECISION + RE-SERIALIZE hold across seeds', () => { for (const seed of [1, 2, 3, 4]) { const g = makeCommunityGraph(seed); const arbiter = g.arbiter; const queries = sample(grantedQuerySet(g), seed); assert.ok(queries.length > 0, `seed ${seed}: empty granted query set`); const live = queries.map(([u, rel, o]) => arbiter.check(u, rel, o).possibility); arbiter.enableCondensedSnapshot(); const buf = arbiter.toSnapshotBinary(); const restored = Arbiter.fromSnapshotBinary(buf); const restored2 = Arbiter.fromSnapshotBinary(restored.toSnapshotBinary()); for (let i = 0; i < queries.length; i++) { const [u, rel, o] = queries[i]; const lv = live[i]; const rv = restored.check(u, rel, o).possibility; const r2v = restored2.check(u, rel, o).possibility; assert.ok( Math.abs(lv - rv) <= TOL, `seed ${seed} ${u} ${rel} ${o}: live=${lv} restored=${rv} exceeds ${TOL}` ); // Allow/deny agreement on the 0.5 threshold, except inside the // quantization band where a flip is permitted. const inBand = Math.abs(lv - 0.5) < QUANT_STEP; if (!inBand) { assert.equal( rv >= 0.5, lv >= 0.5, `seed ${seed} ${u} ${rel} ${o}: decision flipped outside band (live=${lv} restored=${rv})` ); } // Grant parity (possibility > 0): granted queries all carry // possibility >= 0.3, so a flip here would be a real loss. assert.equal(rv > 0, lv > 0, `seed ${seed} ${u} ${rel} ${o}: grant lost in restore`); // Snapshot-of-snapshot is semantically identical. assert.ok( Math.abs(r2v - rv) <= TOL, `seed ${seed} ${u} ${rel} ${o}: re-serialized=${r2v} != first restore=${rv}` ); } } }); });