Files
core/tests/integration/test-bilattice-integration.js
John Dvorak 717ae1031e initial commit: @arbiter/core authorization engine with js-rigor hardening
Zanzibar-style authorization graph engine (direct/chain/TTU/defeasible/
binary modes, condensed snapshots, value relations) with 39 rigor test
campaigns. Includes fixes for snapshot binary writer/reader format
mismatch (snapshot-of-snapshot corruption), possibility write-boundary
validation, empty-graph snapshot serialization, relation lookup cache
direction collision, config-redefinition cache invalidation, binary
threshold semantics, defeasible compiled routing, and comparator
reason whitelisting.
2026-07-31 13:44:06 -07:00

355 lines
14 KiB
JavaScript

/**
* Test Bilattice Integration with Existing Possibilistic Infrastructure
*
* This test demonstrates how bilattice orderings are integrated into our existing
* rule system while maintaining compatibility with possibilistic reasoning.
*/
import { test, describe } from 'node:test';
import assert from 'node:assert';
import {
QualitativeScale,
QualitativeCapacity,
BilatticeOrderings,
getSetKey
} from '../../src/qualitative/index.js';
describe('Bilattice Integration with Possibilistic Infrastructure', () => {
test('should demonstrate BaseRule bilattice-enhanced evidence combination', () => {
const scale = QualitativeScale.fivePoint();
const stateSpace = ['evidence1', 'evidence2', 'evidence3'];
// Create a capacity for bilattice analysis
const qmt = new Map();
qmt.set(getSetKey(new Set(['evidence1'])), 0.75); // High belief evidence
qmt.set(getSetKey(new Set(['evidence2'])), 0.5); // Medium belief evidence
qmt.set(getSetKey(new Set(['evidence3'])), 0.25); // Low belief evidence
qmt.set(getSetKey(new Set(['evidence1', 'evidence2'])), 1); // Combined evidence
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
// Simulate collected values from rule evaluation - using valid fivePoint scale values
const collectedValues = [
{
value: 0.75, // Valid fivePoint scale value
possibility: 0.75, // Valid fivePoint scale value
path: ['user', 'relation1'],
source: { entityKey: 'user1', relation: 'score', step: 0 },
metadata: { timestamp: Date.now(), reliability: 0.9 }
},
{
value: 0.5, // Valid fivePoint scale value
possibility: 0.5, // Valid fivePoint scale value
path: ['user', 'relation2'],
source: { entityKey: 'user2', relation: 'rating', step: 1 },
metadata: { timestamp: Date.now(), reliability: 0.8 }
},
{
value: 0.25, // Valid fivePoint scale value
possibility: 0.25, // Valid fivePoint scale value
path: ['user', 'relation3'],
source: { entityKey: 'user3', relation: 'feedback', step: 2 },
metadata: { timestamp: Date.now(), reliability: 0.7 }
}
];
// Test information-based evidence selection
const epistemicPairs = collectedValues.map(cv => ({
belief: cv.possibility,
disbelief: 1 - cv.possibility
}));
// Create propositions for bilattice analysis
const propositions = collectedValues.map((cv, index) => [`evidence${index + 1}`]);
const mostInformative = BilatticeOrderings.findMostInformative(propositions, capacity);
// The most informative evidence should be the one with highest belief AND disbelief
// Evidence 1: belief=0.75, disbelief=0.25
// Evidence 2: belief=0.5, disbelief=0.5
// Evidence 3: belief=0.25, disbelief=0.75
// Evidence 1 and 2 are incomparable in information ordering (neither dominates)
// The algorithm picks the first one (evidence 1) as default
assert.strictEqual(mostInformative.epistemic.belief, 0.75);
// Disbelief = capacity of the complement: max focal subset of
// {evidence2, evidence3} is {evidence2}=0.5.
assert.strictEqual(mostInformative.epistemic.disbelief, 0.5);
assert.strictEqual(mostInformative.rank, 1);
});
test('should demonstrate qualitative relational comparator with bilattice reasoning', () => {
const scale = QualitativeScale.fivePoint();
// Simulate blurred values from qualitative decay
const blurredValues = [
{
interval: { lower: 0.5, upper: 0.75 },
possibility: 0.75,
originalValue: 0.75,
originalPossibility: 1.0,
timestamp: Date.now(),
relation: 'score',
meta: { periodsElapsed: 1, blurSteps: 1 }
},
{
interval: { lower: 0.25, upper: 0.5 },
possibility: 0.5,
originalValue: 0.5,
originalPossibility: 0.8,
timestamp: Date.now(),
relation: 'rating',
meta: { periodsElapsed: 2, blurSteps: 2 }
}
];
// Test epistemic comparison of blurred values
const epistemicPairs = blurredValues.map(bv => ({
belief: bv.possibility,
disbelief: 1 - bv.possibility
}));
// Create a simple capacity for comparison
const stateSpace = ['blurred1', 'blurred2'];
const qmt = new Map();
qmt.set(getSetKey(new Set(['blurred1'])), 0.75);
qmt.set(getSetKey(new Set(['blurred2'])), 0.5);
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
const comparison = BilatticeOrderings.compareEpistemicStatus(
['blurred1'], ['blurred2'], capacity
);
// blurred1 should be more true than blurred2 (higher capacity value)
assert.ok(comparison.truthOrdering);
assert.strictEqual(comparison.relationship, 'A more true than B');
});
test('should demonstrate defeasible logic with bilattice reasoning', () => {
const scale = QualitativeScale.tenPoint();
// Simulate evidence from different rule types in defeasible logic
const strictEvidence = {
value: 1.0,
possibility: 1.0,
path: ['strict_rule'],
source: { entityKey: 'system', relation: 'strict_check', step: 0 },
metadata: { timestamp: Date.now(), reliability: 1.0, ruleType: 'strict' }
};
const defeasibleEvidence = {
value: 0.8,
possibility: 0.8,
path: ['defeasible_rule'],
source: { entityKey: 'user', relation: 'user_check', step: 1 },
metadata: { timestamp: Date.now(), reliability: 0.9, ruleType: 'defeasible' }
};
const defeaterEvidence = {
value: 0.6,
possibility: 0.6,
path: ['defeater_rule'],
source: { entityKey: 'security', relation: 'security_check', step: 2 },
metadata: { timestamp: Date.now(), reliability: 0.8, ruleType: 'defeater' }
};
const allEvidence = [strictEvidence, defeasibleEvidence, defeaterEvidence];
// Create capacity representing the defeasible logic structure
const stateSpace = ['strict', 'defeasible', 'defeater'];
const qmt = new Map();
qmt.set(getSetKey(new Set(['strict'])), 1.0); // Strict rules have highest priority
qmt.set(getSetKey(new Set(['defeasible'])), 0.8); // Defeasible rules have medium priority
qmt.set(getSetKey(new Set(['defeater'])), 0.6); // Defeaters have lower priority
qmt.set(getSetKey(new Set(['strict', 'defeasible'])), 1.0); // Strict + defeasible = strict wins
qmt.set(getSetKey(new Set(['strict', 'defeater'])), 1.0); // Strict + defeater = strict wins
qmt.set(getSetKey(new Set(['defeasible', 'defeater'])), 0.8); // Defeasible + defeater = defeasible wins
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
// Test information ordering for defeasible logic
const epistemicPairs = allEvidence.map(ev => ({
belief: ev.possibility,
disbelief: 1 - ev.possibility
}));
// Create propositions for bilattice analysis (must match the capacity state space)
const propositions = [['strict'], ['defeasible'], ['defeater']];
const informationRanking = BilatticeOrderings.rankByInformation(propositions, capacity);
// Strict evidence should rank highest in information ordering
const strictRank = informationRanking.find(r => r.epistemic.belief === 1.0)?.rank;
const defeasibleRank = informationRanking.find(r => r.epistemic.belief === 0.8)?.rank;
const defeaterRank = informationRanking.find(r => r.epistemic.belief === 0.6)?.rank;
assert.ok(strictRank <= defeasibleRank);
assert.ok(defeasibleRank <= defeaterRank);
});
test('should demonstrate chain rule with epistemic path analysis', () => {
const scale = QualitativeScale.tenPoint();
// Simulate collected values from a chain traversal
const chainValues = [
{
value: 0.9,
possibility: 0.9,
path: ['user', 'account', 'balance'],
source: { entityKey: 'account1', relation: 'balance', step: 2 },
metadata: { timestamp: Date.now(), reliability: 0.95, pathPossibility: 0.9 }
},
{
value: 0.7,
possibility: 0.7,
path: ['user', 'account', 'credit'],
source: { entityKey: 'account2', relation: 'credit', step: 2 },
metadata: { timestamp: Date.now(), reliability: 0.85, pathPossibility: 0.7 }
},
{
value: 0.5,
possibility: 0.5,
path: ['user', 'account', 'debt'],
source: { entityKey: 'account3', relation: 'debt', step: 2 },
metadata: { timestamp: Date.now(), reliability: 0.75, pathPossibility: 0.5 }
}
];
// Create capacity for path-based epistemic analysis
const stateSpace = ['path1', 'path2', 'path3'];
const qmt = new Map();
qmt.set(getSetKey(new Set(['path1'])), 0.9); // High confidence path
qmt.set(getSetKey(new Set(['path2'])), 0.7); // Medium confidence path
qmt.set(getSetKey(new Set(['path3'])), 0.5); // Low confidence path
qmt.set(getSetKey(new Set(['path1', 'path2'])), 1.0); // Combined high-confidence paths
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
// Test truth ordering for path selection
const pathPropositions = [
['path1'], ['path2'], ['path3']
];
const truthRanking = BilatticeOrderings.rankByTruth(pathPropositions, capacity);
// Path1 should rank highest in truth ordering (highest capacity value)
const path1Rank = truthRanking.find(r => r.capacity === 0.9)?.rank;
const path2Rank = truthRanking.find(r => r.capacity === 0.7)?.rank;
const path3Rank = truthRanking.find(r => r.capacity === 0.5)?.rank;
assert.strictEqual(path1Rank, 1);
assert.ok(path2Rank > path1Rank);
assert.ok(path3Rank > path2Rank);
});
test('should demonstrate hybrid epistemic reasoning', () => {
const scale = QualitativeScale.tenPoint();
// Simulate mixed evidence from different sources
const mixedEvidence = [
{
value: 0.8,
possibility: 0.8,
path: ['direct_evidence'],
source: { entityKey: 'direct', relation: 'observation', step: 0 },
metadata: { timestamp: Date.now(), reliability: 0.9, sourceType: 'direct' }
},
{
value: 0.6,
possibility: 0.6,
path: ['inferred_evidence'],
source: { entityKey: 'inference', relation: 'deduction', step: 1 },
metadata: { timestamp: Date.now(), reliability: 0.7, sourceType: 'inferred' }
},
{
value: 0.4,
possibility: 0.4,
path: ['similarity_evidence'],
source: { entityKey: 'similarity', relation: 'analogy', step: 2 },
metadata: { timestamp: Date.now(), reliability: 0.6, sourceType: 'similarity' }
}
];
// Create capacity for hybrid analysis
const stateSpace = ['direct', 'inferred', 'similarity'];
const qmt = new Map();
qmt.set(getSetKey(new Set(['direct'])), 0.8); // Direct evidence has highest reliability
qmt.set(getSetKey(new Set(['inferred'])), 0.6); // Inferred evidence has medium reliability
qmt.set(getSetKey(new Set(['similarity'])), 0.4); // Similarity evidence has lowest reliability
qmt.set(getSetKey(new Set(['direct', 'inferred'])), 0.9); // Direct + inferred = high confidence
qmt.set(getSetKey(new Set(['direct', 'similarity'])), 0.8); // Direct + similarity = direct dominates
qmt.set(getSetKey(new Set(['inferred', 'similarity'])), 0.6); // Inferred + similarity = inferred dominates
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
// Test comprehensive epistemic comparison
const epistemicPairs = mixedEvidence.map(ev => ({
belief: ev.possibility,
disbelief: 1 - ev.possibility
}));
// Create propositions for bilattice analysis (must match the capacity state space)
const propositions = [['direct'], ['inferred'], ['similarity']];
const informationRanking = BilatticeOrderings.rankByInformation(propositions, capacity);
const truthRanking = BilatticeOrderings.rankByTruth(propositions, capacity);
// Direct evidence should rank highest in both orderings
const directInfoRank = informationRanking.find(r => r.epistemic.belief === 0.8)?.rank;
const directTruthRank = truthRanking.find(r => r.capacity === 0.8)?.rank;
assert.strictEqual(directInfoRank, 1);
assert.strictEqual(directTruthRank, 1);
// Test comprehensive comparison
const comparison = BilatticeOrderings.compareEpistemicStatus(
['direct'], ['inferred'], capacity
);
assert.ok(comparison.truthOrdering);
assert.strictEqual(comparison.relationship, 'A more true than B');
});
test('should maintain backward compatibility with existing possibilistic infrastructure', () => {
const scale = QualitativeScale.tenPoint();
// Test that bilattice reasoning can be disabled and standard OWA fusion still works
const standardEvidence = [
{ value: 0.8, possibility: 0.8, path: ['evidence1'], source: {}, metadata: {} },
{ value: 0.6, possibility: 0.6, path: ['evidence2'], source: {}, metadata: {} },
{ value: 0.4, possibility: 0.4, path: ['evidence3'], source: {}, metadata: {} }
];
// Test standard OWA fusion (bilattice disabled)
const epistemicPairs = standardEvidence.map(ev => ({
belief: ev.possibility,
disbelief: 1 - ev.possibility
}));
// Standard max operation should select the highest value
const maxValue = Math.max(...standardEvidence.map(ev => ev.possibility));
assert.strictEqual(maxValue, 0.8);
// Test that bilattice reasoning can be enabled when needed
const stateSpace = ['ev1', 'ev2', 'ev3'];
const qmt = new Map();
qmt.set(getSetKey(new Set(['ev1'])), 0.8);
qmt.set(getSetKey(new Set(['ev2'])), 0.6);
qmt.set(getSetKey(new Set(['ev3'])), 0.4);
const capacity = new QualitativeCapacity(stateSpace, scale, qmt);
// Create propositions for bilattice analysis
const propositions = standardEvidence.map((ev, index) => [`ev${index + 1}`]);
const mostInformative = BilatticeOrderings.findMostInformative(propositions, capacity);
// Epistemic pairs from the capacity:
// ev1 ({ev1}): belief=0.8, disbelief=gamma({ev2,ev3})=0.6
// ev2 ({ev2}): belief=0.6, disbelief=gamma({ev1,ev3})=0.8
// ev3 ({ev3}): belief=0.4, disbelief=gamma({ev1,ev2})=0.8
// ev1 dominates in belief; the algorithm keeps the first best (ev1).
assert.strictEqual(mostInformative.epistemic.belief, 0.8);
assert.strictEqual(mostInformative.epistemic.disbelief, 0.6);
});
});