js-rigor: greybox fuzzer campaign, config validation, lifecycle positional gates

- fuzzer-mutation-needles.test.js: first greybox-fuzzer campaign (mutation
  + replay-near-failure strategies) hunting cache staleness across config
  kind transitions; pins canonical config shapes ({union:{rules}},
  {exclusion:[a,b]} with probabilistic P(A)*(1-P(B)) semantics) and a
  mirror faithful to the engine's phantom-node no-op rule
- ArbiterConfig.setRelationConfig: clean validation error for non-object
  configs (was cryptic internal TypeError under fuzzed args)
- protocol-snapshot-lifecycle.test.js: beforeStep/afterStep positional
  gates (virgin state, mirror-engine tuple sync)
- dropped complexity-verification attempt: wall-clock e-process too noisy
  at sub-ms operation scale
This commit is contained in:
John Dvorak
2026-07-31 13:59:02 -07:00
parent 717ae1031e
commit 2de5faa7c9
3 changed files with 223 additions and 1 deletions
@@ -189,7 +189,34 @@ const result = await rigor.campaign(
if (action === 'graph.restore') return objects.graph.flag !== 'live' || error !== null;
return true;
}),
rigor.after('graph.setConfig', ({ objects, error }) => error === null || objects.graph.flag === 'restored' ? true : false)
rigor.after('graph.setConfig', ({ objects, error }) => error === null || objects.graph.flag === 'restored' ? true : false),
rigor.beforeStep(0, ({ objects, calls }) => {
const g = objects.graph;
return g.tuples.size === 0 && g.flag === 'live' && calls.length === 0;
}),
rigor.afterStep(2, ({ objects, calls }) => {
const g = objects.graph;
if (g.flag === 'restored') return true;
const engineKeys = new Set();
for (const r of g.engine.relations) {
const srcKey = g.engine.keyByNodeId.get(r.src);
const dstKey = g.engine.keyByNodeId.get(r.dst);
if (srcKey !== undefined && dstKey !== undefined) {
engineKeys.add(`${srcKey}|${r.rel}|${dstKey}`);
}
}
for (const k of g.tuples.keys()) {
if (!engineKeys.has(k)) return false;
}
return g.tuples.size === engineKeys.size;
}),
rigor.beforeStep(4, ({ objects, calls }) => {
const g = objects.graph;
if (g.flag === 'live' || g.flag === 'enabled') {
return g.tuples.size === g.engine.relations.length;
}
return true;
})
])
).run({ effort: 400, seed: 'snapshot-lifecycle-protocol', maxTraceLength: 24 });