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.
This commit is contained in:
John Dvorak
2026-07-31 13:44:06 -07:00
commit 717ae1031e
373 changed files with 654131 additions and 0 deletions
+272
View File
@@ -0,0 +1,272 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Arbiter } from '../../src/core/Arbiter.js';
function setupDirectArbiter() {
const arbiter = new Arbiter();
arbiter.addNode('user:1', 'user');
arbiter.addNode('doc:1', 'doc');
arbiter.setRelationConfig('can_read', { type: 'direct' });
return arbiter;
}
test('ADR-035/042 explainability: audit captures provenance conflict and trust precedence', () => {
const arbiter = setupDirectArbiter();
arbiter.addRelation('user:1', 'can_read', 'doc:1', 0.25);
const partialGraph = {
relations: [
{ src: 'user:1', relation: 'can_read', dst: 'doc:1', possibility: 0.9 }
]
};
const explain = arbiter.explain('user:1', 'can_read', 'doc:1', { partialGraph });
assert.equal(explain.decision.possibility, 0.25);
assert.equal(explain.trace.path[0].source, 'persistent');
const provenance = explain.audit?.provenance;
assert.ok(provenance);
assert.equal(provenance.has_partial_inputs, true);
assert.equal(provenance.partial_fact_used, false);
assert.equal(provenance.effective_source, 'persistent');
assert.equal(provenance.provenance_conflicts.length, 1);
const conflict = provenance.provenance_conflicts[0];
assert.equal(conflict.reason_code, 'higher_trust_source_preferred');
assert.equal(conflict.winner_source, 'persistent');
assert.equal(conflict.loser_source, 'partial');
assert.equal(conflict.relation, 'can_read');
});
test('ADR-035/042 explainability: audit marks partial facts used with no conflict', () => {
const arbiter = setupDirectArbiter();
const partialGraph = {
relations: [
{ src: 'user:1', relation: 'can_read', dst: 'doc:1', possibility: 1.0 }
]
};
const explain = arbiter.explain('user:1', 'can_read', 'doc:1', { partialGraph });
assert.equal(explain.decision.possibility, 1.0);
assert.equal(explain.trace.path[0].source, 'partial');
const provenance = explain.audit?.provenance;
assert.ok(provenance);
assert.equal(provenance.has_partial_inputs, true);
assert.equal(provenance.partial_fact_used, true);
assert.equal(provenance.effective_source, 'partial');
assert.deepEqual(provenance.provenance_conflicts, []);
});
test('ADR-035/042 explainability: chain path conflicts are auditable per edge', () => {
const arbiter = new Arbiter();
arbiter.addNode('user:1', 'user');
arbiter.addNode('device:1', 'device');
arbiter.addNode('account:1', 'account');
arbiter.setRelationConfig('device_link', { type: 'direct' });
arbiter.setRelationConfig('logged_in_as', { type: 'direct' });
arbiter.setRelationConfig('can_login', {
type: 'chain',
steps: [
{ relation: 'device_link', direction: 'out' },
{ relation: 'logged_in_as', direction: 'out' }
]
});
arbiter.addRelation('user:1', 'device_link', 'device:1', 0.2);
arbiter.addRelation('device:1', 'logged_in_as', 'account:1', 0.2);
const partialGraph = {
relations: [
{ src: 'user:1', relation: 'device_link', dst: 'device:1', possibility: 0.95 },
{ src: 'device:1', relation: 'logged_in_as', dst: 'account:1', possibility: 0.95 }
]
};
const explain = arbiter.explain('user:1', 'can_login', 'account:1', { partialGraph });
assert.equal(explain.decision.possibility, 0.2);
const conflicts = explain.audit?.provenance?.provenance_conflicts || [];
assert.ok(conflicts.length >= 2);
const conflictRels = new Set(conflicts.map((c) => c.relation));
assert.ok(conflictRels.has('device_link'));
assert.ok(conflictRels.has('logged_in_as'));
for (const c of conflicts) {
assert.equal(c.reason_code, 'higher_trust_source_preferred');
assert.equal(c.winner_source, 'persistent');
assert.equal(c.loser_source, 'partial');
}
});
test('ADR-035/042 explainability: multi-hop path conflicts are auditable per edge', () => {
const arbiter = new Arbiter();
arbiter.addNode('user:1', 'user');
arbiter.addNode('group:1', 'group');
arbiter.addNode('doc:1', 'doc');
arbiter.setRelationConfig('can_reach', { type: 'multi_hop', relation: 'link', maxDepth: 3 });
arbiter.addRelation('user:1', 'link', 'group:1', 0.3);
arbiter.addRelation('group:1', 'link', 'doc:1', 0.3);
const partialGraph = {
relations: [
{ src: 'user:1', relation: 'link', dst: 'group:1', possibility: 0.9 },
{ src: 'group:1', relation: 'link', dst: 'doc:1', possibility: 0.9 }
]
};
const explain = arbiter.explain('user:1', 'can_reach', 'doc:1', { partialGraph });
assert.equal(explain.decision.possibility, 0.3);
const conflicts = explain.audit?.provenance?.provenance_conflicts || [];
assert.ok(conflicts.length >= 1);
for (const c of conflicts) {
assert.equal(c.relation, 'link');
assert.equal(c.reason_code, 'higher_trust_source_preferred');
assert.equal(c.winner_source, 'persistent');
}
});
test('ADR-035/042 explainability: conflict identities respect key redaction mode', () => {
const arbiter = setupDirectArbiter();
arbiter.addRelation('user:1', 'can_read', 'doc:1', 0.4);
const partialGraph = {
relations: [
{ src: 'user:1', relation: 'can_read', dst: 'doc:1', possibility: 0.95 }
]
};
const explain = arbiter.explain('user:1', 'can_read', 'doc:1', {
partialGraph,
redaction: 'keys'
});
const conflict = explain.audit?.provenance?.provenance_conflicts?.[0];
assert.ok(conflict);
assert.notEqual(conflict.src, 'user:1');
assert.notEqual(conflict.object, 'doc:1');
assert.equal(conflict.src.length, 64);
assert.equal(conflict.object.length, 64);
assert.equal(explain.request.userKey, conflict.src);
assert.equal(explain.request.objectKey, conflict.object);
});
test('ADR-035/042 explainability: hash redaction keeps keys and still includes hashes', () => {
const arbiter = setupDirectArbiter();
arbiter.addRelation('user:1', 'can_read', 'doc:1', 0.4);
const partialGraph = {
relations: [
{ src: 'user:1', relation: 'can_read', dst: 'doc:1', possibility: 0.95 }
]
};
const explain = arbiter.explain('user:1', 'can_read', 'doc:1', {
partialGraph,
redaction: 'hash'
});
const conflict = explain.audit?.provenance?.provenance_conflicts?.[0];
assert.ok(conflict);
assert.equal(conflict.src, 'user:1');
assert.equal(conflict.object, 'doc:1');
assert.equal(typeof explain.request.userKeyHash, 'string');
assert.equal(typeof explain.request.objectKeyHash, 'string');
assert.equal(explain.request.userKeyHash.length, 64);
assert.equal(explain.request.objectKeyHash.length, 64);
});
test('ADR-035/042 explainability: audit provenance contract has stable shape', () => {
const arbiter = setupDirectArbiter();
arbiter.addRelation('user:1', 'can_read', 'doc:1', 0.4);
const partialGraph = {
relations: [
{ src: 'user:1', relation: 'can_read', dst: 'doc:1', possibility: 0.95 }
]
};
const explain = arbiter.explain('user:1', 'can_read', 'doc:1', { partialGraph });
const provenance = explain.audit?.provenance;
assert.ok(provenance);
assert.deepEqual(Object.keys(provenance).sort(), [
'effective_source',
'has_partial_inputs',
'partial_fact_used',
'policy_conflicts',
'provenance_conflicts'
,
'used_facts'
]);
assert.equal(typeof provenance.has_partial_inputs, 'boolean');
assert.equal(typeof provenance.partial_fact_used, 'boolean');
assert.equal(typeof provenance.effective_source, 'string');
assert.ok(Array.isArray(provenance.provenance_conflicts));
assert.ok(Array.isArray(provenance.used_facts));
assert.ok(Array.isArray(provenance.policy_conflicts));
assert.ok(provenance.provenance_conflicts.length >= 1);
const conflict = provenance.provenance_conflicts[0];
assert.deepEqual(Object.keys(conflict).sort(), [
'decision_possibility',
'loser_source',
'object',
'partial_layer_name',
'partial_possibility',
'partial_reducer_applied',
'partial_source_class',
'persistent_possibility',
'reason_code',
'relation',
'resolution',
'rule_type',
'src',
'winner_source'
]);
});
test('ADR-042 explainability: used facts and policy conflicts are surfaced', () => {
const arbiter = new Arbiter();
arbiter.addNode('user:1', 'user');
arbiter.addNode('doc:1', 'doc');
// The direct rule checks 'gateway_context_ref', which IS allowlisted at
// the request_observed layer (can_read is not).
arbiter.setRelationConfig('can_read', { type: 'direct', relation: 'gateway_context_ref' });
const partialGraph = {
options: {
reducers: {
delegated_authority: 'strongest'
}
},
relations: [
{
src: 'user:1',
relation: 'gateway_context_ref',
dst: 'doc:1',
possibility: 1.0,
layer_name: 'request_observed',
source_class: 'gateway_observed'
}
]
};
const explain = arbiter.explain('user:1', 'can_read', 'doc:1', { partialGraph });
const provenance = explain.audit?.provenance;
assert.ok(provenance);
const used = provenance.used_facts[0];
assert.ok(used);
assert.equal(used.layer_name, 'request_observed');
assert.equal(used.source_class, 'gateway_observed');
assert.equal(used.source, 'partial');
assert.equal(used.used, true);
const policy = provenance.policy_conflicts;
assert.ok(policy.some((p) => p.kind === 'invalid_reducer_config'));
});
@@ -0,0 +1,62 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import fc from 'fast-check';
import { Arbiter } from '../../src/core/Arbiter.js';
import { PartialGraphContext } from '../../src/core/PartialGraphContext.js';
import { getTrustScore } from '../../src/core/partial-graph/reducers.js';
const LAYERS = [
'token_projection',
'workflow_overlay',
'request_observed',
'attested_context',
'challenge_evidence',
'provenance_overlay',
'caller_declared'
];
test('fast-check partial context: stacked same-triple facts honor latest reducer within top trust', () => {
fc.assert(
fc.property(
fc.array(
fc.record({
ts: fc.integer({ min: 1, max: 1000000 }),
layer: fc.constantFrom(...LAYERS),
value: fc.integer({ min: 1, max: 1000000 })
}),
{ minLength: 2, maxLength: 20 }
),
(inputs) => {
const arbiter = new Arbiter();
arbiter.addNode('request:1', 'request');
arbiter.addNode('time:now', 'timestamp');
const relations = inputs.map((x) => ({
src: 'request:1',
relation: 'request_has_timestamp',
dst: 'time:now',
value: x.value,
updated_last_at: x.ts,
changed_last_at: x.ts,
layer_name: x.layer
}));
const ctx = new PartialGraphContext(arbiter, {
options: { reducers: { request_has_timestamp: 'latest' } },
relations
});
const srcId = arbiter.nodeIdByKey.get('request:1');
const dstId = arbiter.nodeIdByKey.get('time:now');
const direct = ctx.getDirectRelation(srcId, 'request_has_timestamp', dstId);
assert.ok(direct);
const topTrust = Math.max(...inputs.map((x) => getTrustScore(x.layer)));
const top = inputs.filter((x) => getTrustScore(x.layer) === topTrust);
const expectedTs = Math.max(...top.map((x) => x.ts));
assert.equal(direct.updated_last_at, expectedTs);
}
),
{ numRuns: 150 }
);
});
@@ -0,0 +1,89 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Arbiter } from '../../src/core/Arbiter.js';
import { PartialGraphContext } from '../../src/core/PartialGraphContext.js';
function setupArbiter() {
const arbiter = new Arbiter();
arbiter.addNode('request:1', 'request');
arbiter.addNode('time:now', 'timestamp');
return arbiter;
}
test('ADR-042 reducers are first-class in PartialGraphContext conflict resolution', () => {
const arbiter = setupArbiter();
const ctx = new PartialGraphContext(arbiter, {
options: {
reducers: {
request_has_timestamp: 'latest'
}
},
relations: [
{
src: 'request:1',
relation: 'request_has_timestamp',
dst: 'time:now',
value: 100,
updated_last_at: 100,
layer_name: 'request_observed'
},
{
src: 'request:1',
relation: 'request_has_timestamp',
dst: 'time:now',
value: 200,
updated_last_at: 200,
layer_name: 'request_observed'
}
]
});
const srcId = arbiter.nodeIdByKey.get('request:1');
const dstId = arbiter.nodeIdByKey.get('time:now');
const direct = ctx.getDirectRelation(srcId, 'request_has_timestamp', dstId);
assert.ok(direct);
assert.equal(direct.value, 200);
const audit = ctx.getReducerAudit();
assert.ok(audit.some((entry) => entry.type === 'relation_conflict_resolved'));
});
test('ADR-042 strict conflict mode rejects unresolved same-triple conflicts', () => {
const arbiter = setupArbiter();
assert.throws(() => {
new PartialGraphContext(arbiter, {
options: {
strictConflicts: true
},
relations: [
{
src: 'request:1',
relation: 'request_has_id',
dst: 'time:now',
value: 1,
layer_name: 'request_observed'
},
{
src: 'request:1',
relation: 'request_has_id',
dst: 'time:now',
value: 2,
layer_name: 'request_observed'
}
]
});
}, /partial_graph_conflict_without_reducer/);
});
test('ADR-042 invalid reducer configuration is reported in reducer audit', () => {
const arbiter = setupArbiter();
const ctx = new PartialGraphContext(arbiter, {
options: {
reducers: {
delegated_authority: 'strongest'
}
},
relations: []
});
const audit = ctx.getReducerAudit();
assert.ok(audit.some((entry) => entry.type === 'invalid_reducer_config'));
});
@@ -0,0 +1,107 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Arbiter } from '../../src/core/Arbiter.js';
test('ADR-035/042: first-class core policy config sets conflict mode and reducers', () => {
const arbiter = new Arbiter();
const initial = arbiter.getPartialGraphPolicy();
assert.equal(initial.conflict_mode, 'deterministic');
arbiter.setPartialGraphPolicy({
conflict_mode: 'strict',
reducers: {
request_has_timestamp: 'latest'
}
});
const policy = arbiter.getPartialGraphPolicy();
assert.equal(policy.conflict_mode, 'strict');
assert.equal(policy.reducers.request_has_timestamp, 'latest');
const snapshot = arbiter.getPartialGraphPolicySnapshot();
assert.equal(snapshot.conflict_mode, 'strict');
assert.equal(snapshot.reducer_count, 1);
});
test('ADR-035/042: DSL relation config can declare partial-graph reducer policy', () => {
const arbiter = new Arbiter();
arbiter.setRelationConfig('request_has_timestamp', {
type: 'direct',
partial_graph: {
reducer: 'latest'
}
});
const policy = arbiter.getPartialGraphPolicy();
assert.equal(policy.reducers.request_has_timestamp, 'latest');
});
test('ADR-035/042: policy-level strict conflict mode is enforced by default', () => {
const arbiter = new Arbiter({
partialGraphPolicy: {
conflict_mode: 'strict'
}
});
arbiter.addNode('user:1', 'user');
arbiter.addNode('obj:1', 'obj');
arbiter.setRelationConfig('request_has_id', { type: 'direct' });
assert.throws(() => {
arbiter._createPartialGraphContext({
relations: [
{
src: 'user:1',
relation: 'request_has_id',
dst: 'obj:1',
value: 1,
layer_name: 'request_observed'
},
{
src: 'user:1',
relation: 'request_has_id',
dst: 'obj:1',
value: 2,
layer_name: 'request_observed'
}
]
});
}, /partial_graph_conflict_without_reducer/);
});
test('ADR-035/042: request options can override core policy conflict mode', () => {
const arbiter = new Arbiter({
partialGraphPolicy: {
conflict_mode: 'strict'
}
});
arbiter.addNode('user:1', 'user');
arbiter.addNode('obj:1', 'obj');
arbiter.setRelationConfig('request_has_id', { type: 'direct' });
const context = arbiter._createPartialGraphContext({
options: {
conflict_mode: 'deterministic'
},
relations: [
{
src: 'user:1',
relation: 'request_has_id',
dst: 'obj:1',
value: 1,
layer_name: 'request_observed'
},
{
src: 'user:1',
relation: 'request_has_id',
dst: 'obj:1',
value: 2,
layer_name: 'request_observed'
}
]
});
const srcId = arbiter.nodeIdByKey.get('user:1');
const dstId = arbiter.nodeIdByKey.get('obj:1');
const rel = context.getDirectRelation(srcId, 'request_has_id', dstId);
assert.ok(rel);
});
@@ -0,0 +1,127 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import {
buildLayerConformanceMatrix,
getAllowedRelationsForLayer,
getAllowedCategoriesForLayer,
getProhibitedCategoriesForLayer,
getRelationCategory,
getSupportedReducersForRelation,
isReducerAllowedForRelation,
listLayerNames,
resolveLayerName,
validateReducerConfig,
validateClaimsForLayer
} from '../../../src/core/partial-graph/layer-registry.js';
test('ADR-042: layer registry exposes all named layers and aliases', () => {
const expected = [
'token_projection',
'workflow_overlay',
'request_observed',
'attested_context',
'caller_declared',
'challenge_evidence',
'provenance_overlay',
'delegation_evidence',
'approval_evidence'
];
assert.deepEqual(listLayerNames(), expected);
assert.equal(resolveLayerName(' workflow_claims '), 'workflow_overlay');
assert.equal(resolveLayerName('REQUEST_OBSERVED'), 'request_observed');
assert.equal(resolveLayerName(''), null);
});
test('ADR-042: conformance matrix is complete and enforced for all layers', () => {
const matrix = buildLayerConformanceMatrix();
const names = new Set(listLayerNames());
assert.equal(matrix.layer_count, names.size);
assert.ok(Array.isArray(matrix.layers));
assert.equal(matrix.layers.length, names.size);
for (const layer of matrix.layers) {
assert.ok(names.has(layer.layer_name));
assert.equal(layer.conformance, 'enforced');
assert.ok(Array.isArray(layer.allowed_relations));
assert.ok(layer.allowed_relations.length > 0);
assert.deepEqual(layer.allowed_relations, getAllowedRelationsForLayer(layer.layer_name));
assert.deepEqual(layer.allowed_categories, getAllowedCategoriesForLayer(layer.layer_name));
assert.deepEqual(layer.prohibited_categories, getProhibitedCategoriesForLayer(layer.layer_name));
}
});
test('ADR-042: relation allowlists are enforced with accepted/rejected claims', () => {
const ok = validateClaimsForLayer('request_observed', [
{ relation: 'from_ip', object: 'ip:10.0.0.1' },
{ relation: 'request_has_id', object: 'req:1' }
]);
assert.equal(ok.valid, true);
assert.equal(ok.accepted_claims.length, 2);
assert.equal(ok.rejected_claims.length, 0);
const bad = validateClaimsForLayer('caller_declared', [
{ relation: 'client_fingerprint_evidence', object: 'fp:abc' },
{ relation: 'delegated_authority', object: 'resource:x' }
]);
assert.equal(bad.valid, false);
assert.equal(bad.accepted_claims.length, 1);
assert.equal(bad.rejected_claims.length, 1);
assert.deepEqual(bad.disallowed_relations, ['delegated_authority']);
});
test('ADR-042: invalid layers and caller allowlist restrictions are enforced', () => {
const invalid = validateClaimsForLayer('not_a_layer', [
{ relation: 'from_ip', object: 'ip:10.0.0.1' }
]);
assert.equal(invalid.valid, false);
assert.equal(invalid.error, 'invalid_layer_type');
assert.deepEqual(invalid.allowed_layers, listLayerNames());
const callerRestricted = validateClaimsForLayer(
'token_projection',
[
{ relation: 'workflow_handoff', object: 'wf:1' },
{ relation: 'delegated_authority', object: 'resource:1' }
],
['workflow_handoff']
);
assert.equal(callerRestricted.valid, false);
assert.equal(callerRestricted.accepted_claims.length, 1);
assert.equal(callerRestricted.rejected_claims.length, 1);
assert.deepEqual(callerRestricted.disallowed_relations, ['delegated_authority']);
});
test('ADR-042: category-level conformance is enforced', () => {
assert.equal(getRelationCategory('from_ip'), 'request_metadata');
assert.equal(getRelationCategory('workflow_handoff'), 'workflow_authority');
assert.equal(getRelationCategory('unknown_relation'), 'unclassified');
const result = validateClaimsForLayer('caller_declared', [
{ relation: 'workflow_handoff', object: 'wf:1' }
]);
assert.equal(result.valid, false);
assert.equal(result.category_conflicts.length, 1);
assert.equal(result.category_conflicts[0].category, 'workflow_authority');
});
test('ADR-042: reducer support is relation-scoped and validated', () => {
assert.deepEqual(getSupportedReducersForRelation('request_has_timestamp'), ['latest', 'first', 'unique']);
assert.equal(isReducerAllowedForRelation('request_has_timestamp', 'latest'), true);
assert.equal(isReducerAllowedForRelation('request_has_timestamp', 'union_refs'), false);
const validation = validateReducerConfig({
request_has_timestamp: 'latest',
provenance_hash_ref: 'union_refs',
delegated_authority: 'strongest'
});
assert.equal(validation.ok, false);
assert.deepEqual(validation.valid, {
request_has_timestamp: 'latest',
provenance_hash_ref: 'union_refs'
});
assert.equal(validation.invalid.length, 1);
assert.equal(validation.invalid[0].relation, 'delegated_authority');
});
@@ -0,0 +1,69 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import fc from 'fast-check';
import { reduceConflictingFacts, listSupportedReducers, getTrustScore } from '../../../src/core/partial-graph/reducers.js';
const LAYERS = [
'token_projection',
'workflow_overlay',
'request_observed',
'attested_context',
'challenge_evidence',
'provenance_overlay',
'caller_declared'
];
const REDUCERS = listSupportedReducers();
const factArb = fc.record({
possibility: fc.double({ min: 0, max: 1, noNaN: true, noDefaultInfinity: true }),
reliability: fc.double({ min: 0, max: 1, noNaN: true, noDefaultInfinity: true }),
value: fc.oneof(fc.constant(null), fc.integer({ min: -1000, max: 1000 })),
updated_last_at: fc.integer({ min: 0, max: 1000000 }),
layer_name: fc.constantFrom(...LAYERS)
}).map((x) => ({
src: 1,
rel: 'request_has_timestamp',
dst: 2,
possibility: x.possibility,
reliability: x.reliability,
value: x.value,
updated_last_at: x.updated_last_at,
changed_last_at: x.updated_last_at,
attributes: null,
layer_name: x.layer_name,
source: 'partial'
}));
test('fast-check reducers: winner never comes from lower trust layer', () => {
fc.assert(
fc.property(
fc.array(factArb, { minLength: 2, maxLength: 15 }),
fc.constantFrom(...REDUCERS),
(facts, reducer) => {
const reduced = reduceConflictingFacts(facts, reducer);
if (!reduced.fact) return true;
const maxTrust = facts.reduce((m, f) => Math.max(m, getTrustScore(f.layer_name)), -Infinity);
return getTrustScore(reduced.fact.layer_name) === maxTrust;
}
),
{ numRuns: 200 }
);
});
test('fast-check reducers: latest selects max timestamp within top trust class', () => {
fc.assert(
fc.property(
fc.array(factArb, { minLength: 2, maxLength: 15 }),
(facts) => {
const reduced = reduceConflictingFacts(facts, 'latest');
if (!reduced.fact) return true;
const winnerTrust = getTrustScore(reduced.fact.layer_name);
const top = facts.filter((f) => getTrustScore(f.layer_name) === winnerTrust);
const expected = top.reduce((m, f) => Math.max(m, f.updated_last_at || 0), -Infinity);
return (reduced.fact.updated_last_at || 0) === expected;
}
),
{ numRuns: 200 }
);
});
+82
View File
@@ -0,0 +1,82 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { reduceConflictingFacts, listSupportedReducers } from '../../../src/core/partial-graph/reducers.js';
function fact(overrides = {}) {
return {
src: 1,
rel: 'request_has_timestamp',
dst: 2,
possibility: 0.7,
reliability: 0.9,
value: 10,
attributes: null,
updated_last_at: 100,
changed_last_at: 100,
layer_name: 'request_observed',
source: 'partial',
...overrides
};
}
test('ADR-042 reducer set is explicitly supported', () => {
assert.deepEqual(listSupportedReducers().sort(), [
'dedupe',
'first',
'ignore_conflict',
'latest',
'max_value',
'min_value',
'strongest',
'union_refs',
'unique',
'weakest'
]);
});
test('ADR-042 trust filtering applies before reducer selection', () => {
const highTrust = fact({ possibility: 0.2, layer_name: 'token_projection' });
const lowTrust = fact({ possibility: 0.9, layer_name: 'caller_declared' });
const reduced = reduceConflictingFacts([lowTrust, highTrust], 'strongest');
assert.equal(reduced.fact.possibility, 0.2);
});
test('ADR-042 ignore_conflict returns absent on incompatible same-trust facts', () => {
const a = fact({ value: 10 });
const b = fact({ value: 20 });
const reduced = reduceConflictingFacts([a, b], 'ignore_conflict');
assert.equal(reduced.fact, null);
assert.equal(reduced.audit.reason_code, 'ignore_conflict_absent');
});
test('ADR-042 latest reducer picks freshest same-trust fact', () => {
const older = fact({ value: 10, updated_last_at: 100 });
const newer = fact({ value: 20, updated_last_at: 200 });
const reduced = reduceConflictingFacts([older, newer], 'latest');
assert.equal(reduced.fact.value, 20);
});
test('ADR-042 min/max reducers select bounded numeric values', () => {
const a = fact({ value: 10 });
const b = fact({ value: 20 });
const minReduced = reduceConflictingFacts([a, b], 'min_value');
const maxReduced = reduceConflictingFacts([a, b], 'max_value');
assert.equal(minReduced.fact.value, 10);
assert.equal(maxReduced.fact.value, 20);
});
test('ADR-042 strongest/weakest are deterministic after trust filtering', () => {
const weaker = fact({ possibility: 0.3, reliability: 0.8 });
const stronger = fact({ possibility: 0.9, reliability: 1.0 });
const strongest = reduceConflictingFacts([weaker, stronger], 'strongest');
const weakest = reduceConflictingFacts([weaker, stronger], 'weakest');
assert.equal(strongest.fact.possibility, 0.9);
assert.equal(weakest.fact.possibility, 0.3);
});
test('ADR-042 union_refs merges reference sets without authority values', () => {
const a = fact({ rel: 'provenance_hash_ref', value: null, attributes: { refs: ['hash:a'] } });
const b = fact({ rel: 'provenance_hash_ref', value: null, attributes: { refs: ['hash:b', 'hash:a'] } });
const reduced = reduceConflictingFacts([a, b], 'union_refs');
assert.deepEqual(reduced.fact.attributes.refs, ['hash:a', 'hash:b']);
});
+241
View File
@@ -0,0 +1,241 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { Arbiter } from '../../src/core/Arbiter.js';
test('reducers: direct rule uses reduced same-triple fact', () => {
const arbiter = new Arbiter();
arbiter.addNode('user:1', 'user');
arbiter.addNode('ts:1', 'timestamp');
arbiter.setRelationConfig('request_has_timestamp', { type: 'direct' });
const partialGraph = {
options: { reducers: { request_has_timestamp: 'latest' } },
relations: [
{
src: 'user:1',
relation: 'request_has_timestamp',
dst: 'ts:1',
possibility: 0.9,
value: 100,
updated_last_at: 100,
layer_name: 'request_observed'
},
{
src: 'user:1',
relation: 'request_has_timestamp',
dst: 'ts:1',
possibility: 0.2,
value: 200,
updated_last_at: 200,
layer_name: 'request_observed'
}
]
};
const result = arbiter.check('user:1', 'request_has_timestamp', 'ts:1', { partialGraph });
assert.equal(result.possibility, 0.2);
});
test('reducers: chain rule consumes reduced edge selections', () => {
const arbiter = new Arbiter();
arbiter.addNode('user:1', 'user');
arbiter.addNode('mid:1', 'mid');
arbiter.addNode('obj:1', 'obj');
arbiter.setRelationConfig('request_has_timestamp', { type: 'direct' });
arbiter.setRelationConfig('caller_risk_hint', { type: 'direct' });
arbiter.setRelationConfig('can_chain', {
type: 'chain',
steps: [
{ relation: 'request_has_timestamp', direction: 'out' },
{ relation: 'caller_risk_hint', direction: 'out' }
]
});
const partialGraph = {
options: {
reducers: {
request_has_timestamp: 'latest',
caller_risk_hint: 'strongest'
}
},
relations: [
{
src: 'user:1',
relation: 'request_has_timestamp',
dst: 'mid:1',
possibility: 0.95,
updated_last_at: 100,
layer_name: 'request_observed'
},
{
src: 'user:1',
relation: 'request_has_timestamp',
dst: 'mid:1',
possibility: 0.2,
updated_last_at: 200,
layer_name: 'request_observed'
},
{
src: 'mid:1',
relation: 'caller_risk_hint',
dst: 'obj:1',
possibility: 0.9,
layer_name: 'caller_declared'
}
]
};
const result = arbiter.check('user:1', 'can_chain', 'obj:1', { partialGraph });
assert.equal(result.possibility, 0.2);
});
test('reducers: multi-hop rule consumes reduced edge selections', () => {
const arbiter = new Arbiter();
arbiter.addNode('user:1', 'user');
arbiter.addNode('hop:1', 'hop');
arbiter.addNode('obj:1', 'obj');
arbiter.setRelationConfig('caller_risk_hint', { type: 'direct' });
arbiter.setRelationConfig('can_reach', { type: 'multi_hop', relation: 'caller_risk_hint', maxDepth: 3 });
const partialGraph = {
options: { reducers: { caller_risk_hint: 'strongest' } },
relations: [
{
src: 'user:1',
relation: 'caller_risk_hint',
dst: 'hop:1',
possibility: 0.2,
layer_name: 'caller_declared'
},
{
src: 'user:1',
relation: 'caller_risk_hint',
dst: 'hop:1',
possibility: 0.85,
layer_name: 'caller_declared'
},
{
src: 'hop:1',
relation: 'caller_risk_hint',
dst: 'obj:1',
possibility: 0.9,
layer_name: 'caller_declared'
}
]
};
const result = arbiter.check('user:1', 'can_reach', 'obj:1', { partialGraph });
assert.equal(result.possibility, 0.85);
});
test('reducers: tuple_to_userset rule consumes reduced userset relation', () => {
const arbiter = new Arbiter();
arbiter.addNode('user:1', 'user');
arbiter.addNode('group:1', 'group');
arbiter.addNode('doc:1', 'doc');
arbiter.setRelationConfig('request_has_timestamp', { type: 'direct' });
arbiter.setRelationConfig('request_has_id', { type: 'direct' });
arbiter.setRelationConfig('can_access', {
type: 'tuple_to_userset',
tuplesetRelation: 'request_has_id',
computedRelation: 'request_has_timestamp',
reverse: false
});
const partialGraph = {
options: { reducers: { request_has_timestamp: 'latest' } },
relations: [
{
src: 'doc:1',
relation: 'request_has_id',
dst: 'group:1',
possibility: 1,
layer_name: 'request_observed'
},
{
src: 'user:1',
relation: 'request_has_timestamp',
dst: 'group:1',
possibility: 0.95,
updated_last_at: 100,
layer_name: 'request_observed'
},
{
src: 'user:1',
relation: 'request_has_timestamp',
dst: 'group:1',
possibility: 0.25,
updated_last_at: 200,
layer_name: 'request_observed'
}
]
};
const result = arbiter.check('user:1', 'can_access', 'doc:1', { partialGraph });
assert.equal(result.possibility, 0.25);
});
test('reducers: relational comparator uses reduced operand values', () => {
const arbiter = new Arbiter();
arbiter.addNode('user:1', 'user');
arbiter.addNode('feature:1', 'feature');
arbiter.setRelationConfig('request_has_timestamp', { type: 'direct' });
arbiter.setRelationConfig('request_has_id', { type: 'direct' });
arbiter.setRelationConfig('can_pay', {
type: 'relational_comparator',
left: {
rule: { type: 'direct', relation: 'request_has_timestamp' },
extractValue: true,
aggregation: 'max',
decayRate: 0,
decayFunction: 'rational'
},
right: {
rule: { type: 'direct', relation: 'request_has_id', evaluateFrom: 'object' },
extractValue: true,
aggregation: 'min',
decayRate: 0,
decayFunction: 'rational',
evaluateFrom: 'object'
},
comparator: '>=',
fallbackBehavior: 'deny'
});
const partialGraph = {
options: { reducers: { request_has_timestamp: 'latest' } },
relations: [
{
src: 'user:1',
relation: 'request_has_timestamp',
dst: 'feature:1',
value: 120,
possibility: 1,
updated_last_at: 100,
layer_name: 'request_observed'
},
{
src: 'user:1',
relation: 'request_has_timestamp',
dst: 'feature:1',
value: 20,
possibility: 1,
updated_last_at: 200,
layer_name: 'request_observed'
},
{
src: 'feature:1',
relation: 'request_has_id',
dst: 'feature:1',
value: 50,
possibility: 1,
layer_name: 'request_observed'
}
]
};
const result = arbiter.check('user:1', 'can_pay', 'feature:1', { partialGraph });
assert.ok(result.possibility < 0.5);
});