js-rigor: fix TTU computed-join possibility loss and logical cache staleness
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.
This commit is contained in:
@@ -235,14 +235,23 @@ export class TupleToUsersetRule extends BaseRule {
|
||||
|
||||
if (combinedPossibility >= minPossibility) {
|
||||
const path = {
|
||||
possibility: combinedPossibility,
|
||||
reliability: combinedReliability,
|
||||
intermediateKey,
|
||||
tuplesetPossibility: resolvePossibility(tupleEdge.possibility),
|
||||
computedPossibility: res.possibility,
|
||||
combinedPossibility,
|
||||
combinedReliability
|
||||
...(!useLightweightPaths && includeMeta && {
|
||||
meta: {
|
||||
intermediateKey,
|
||||
pathType: 'direct_tuple',
|
||||
tuplesetRelation: { relation: rule.tuplesetRelation, possibility: resolvePossibility(tupleEdge.possibility), reliability: resolveReliability(tupleEdge.reliability) },
|
||||
computedRelation: { relation: rule.computedRelation, possibility: res.possibility, reliability: res.reliability, meta: res.meta }
|
||||
}
|
||||
}),
|
||||
...(!useLightweightPaths && collectValues && { collectedValue: intermediateKey })
|
||||
};
|
||||
allValidPaths.push(path);
|
||||
if (!bestPath || combinedPossibility > bestPath.combinedPossibility) {
|
||||
if (!bestPath || combinedPossibility > bestPath.possibility) {
|
||||
bestPath = path;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -519,7 +519,12 @@ export class Arbiter {
|
||||
|
||||
_collectRelationUsages(rule, acc = new Map()) {
|
||||
if (!rule || typeof rule !== 'object') return acc;
|
||||
if (rule.type === 'direct') {
|
||||
if (rule.type === 'direct' || (!rule.type && !rule.union && !rule.intersection && !rule.exclusion && (rule.relation || rule.rel || rule.label || rule.name))) {
|
||||
// Shorthand operand objects ({ relation: 'owner' } inside union/
|
||||
// intersection/exclusion) are direct usages too; failing to register
|
||||
// them left the dependency index empty for logical rules, so
|
||||
// invalidateRuleResultCacheByRelation never cleared their cached
|
||||
// decisions after relation writes.
|
||||
const relName = rule.relation || rule.rel || rule.label || rule.name;
|
||||
if (relName) {
|
||||
const reverse = rule.reverse === true;
|
||||
|
||||
Reference in New Issue
Block a user