rigor: anti-vacuity guards, snapshot O(n), mutation crucible

Three improvements over the complex-graph crucibles:

1. Anti-vacuity guards (assertRealVerdict): rigor's complexity verdict
   PASSES on zero observations — a broken action (missing import, wrong
   args shape) silently goes green. Every complexity verdict now asserts
   observationCount >= 50, costSource == expected, and calibrated ==
   true, so a vacuous verdict is a test failure.

2. Snapshot complexity: serialized snapshot BYTE SIZE is O(n) in graph
   size, verified deterministically (build and restore round-trip).
   Wall-clock timing at sub-ms scale is pure jitter for the e-process
   spread check (verified empirically — buildTime O(n) failed on spread
   while buildBytes passed); latency stays covered by benchmark
   percentiles.

3. complex-graph-mutation-crucible: MUTATION-FRESHNESS — random edge
   removals/additions on community + scale-free graphs, normal/binary
   agreement re-checked after EVERY mutation, stale grants and missing
   fresh grants are failures. Two real findings during bring-up, both
   fixture bugs rather than engine bugs:
   - the scale-free generator returned a raw edge COUNT as 'relations'
     while other generators returned edge arrays (now null, consistent
     with dense-adversarial; the crucible walks the arbiter's store)
   - arbiter.relations stores NUMERIC ids, so removals must resolve
     string keys to ids before matching (string-key comparison silently
     no-oped, looking like a stale grant)

Snapshot read-only semantics documented in the crucible: enableCondensed-
Snapshot flips the engine to read-only permanently, so mutation crucibles
exercise the writable path, and frozen-snapshot properties stay with the
snapshot-parity suites. Rigor 239/239, full suite 841/779/0.
This commit is contained in:
John Dvorak
2026-08-02 14:33:31 -07:00
parent f0aefe4ba6
commit 1a2a6fc22e
3 changed files with 164 additions and 3 deletions
+1 -1
View File
@@ -229,7 +229,7 @@ export function makeScaleFreeGraph(seed = 42, opts = {}) {
arbiter,
users: userKeys,
resources: resourceKeys,
relations: edges,
relations: null,
meta: { kind: 'scale-free', users, resources, edges }
};
}