Two real bugs found by the new rule-kind x partial-graph parity campaign:
- TupleToUsersetRule 'computed' join mode (computed side has fewer
intermediates than the tupleset side) pushed path objects carrying
combinedPossibility, but _buildFinalResult reads path.possibility —
every valid TTU grant in that mode silently returned 0, in persistent
and partial contexts alike.
- _collectRelationUsages only registered explicit type:'direct' children,
so shorthand logical operands ({ relation: 'owner' } inside union/
intersection/exclusion) left the dependency index empty: writes to a
base relation never invalidated cached logical decisions, and a check
performed before an add kept serving its stale result forever.
New campaign rule-kind-partial-parity.test.js pins the full kind x
persistent/partial matrix (direct, chain, multi_hop, TTU out/in/reverse,
parent, computed, defeasible, union, exclusion, comparator, challenge,
binary) plus seeded differential properties for TTU, comparator, and
exclusion; artifact persistence disabled to avoid disk bloat.
The binary branch of AuthorizationChecker.check rebuilt its options with a
fixed six-field object, silently discarding partialGraphContext (and any
other caller option) — binary checks denied grants the normal path
allowed. Now spreads all caller options through. binary-partial-parity
pins: partial grants above the threshold allow, below deny, persistent
wins over partial, and binary decisions agree with normal decisions on
the same overlay.
Engine fixes:
- RelationUpdates.updateRelationsBatch: invalidate arbiter-level caches
(rule result cache, ChainRule caches, direct-check cache) per affected
relation — batch updates bypassed Arbiter.addRelation and served stale
decisions after batch modify/swap with warm caches
- updateRelationsBatchTransactional rollback: new Map(Set) crashed with
'Iterator value is not an entry object' — fixed to new Set
- RelationalComparatorRule: value extraction (direct-list and cached
direct paths) now gates on valueManager._isValueExpired — TTL-expired
values no longer feed comparator decisions
Campaigns:
- value-freshness-parity.test.js: batch modify/swap/tx rollback freshness
with comparator mirror (batch MODIFY of a missing relation is a silent
no-op — pinned)
- ttl-expiry-parity.test.js: injected-clock TTL expiry through the
comparator path (exact parity with caching off; bounded staleness with
caching on), faithful changed_last_at mirror semantics