js-rigor: partial-graph policy limits now enforce; binary-partial pins

- Arbiter constructor and setPartialGraphPolicy dropped maxNodes,
  maxRelations, and reservedRelations — the partial-graph context always
  saw the 1000/2000 defaults, silently disabling configured DoS guards.
  Both paths now carry the limits through; policy limits test pins
  constructor + setter enforcement and custom reservedRelations.
This commit is contained in:
John Dvorak
2026-07-31 15:34:45 -07:00
parent 8f863275c2
commit ad9aa22225
3 changed files with 51 additions and 2 deletions
+7 -1
View File
@@ -130,7 +130,13 @@ export class Arbiter {
|| 'deterministic',
reducers: {
...(options?.partialGraphPolicy?.reducers || {})
}
},
// Size/reserved limits must survive construction: the partial-graph
// context reads them from the policy, and dropping them silently
// disabled configured DoS guards (defaults 1000/2000 applied).
maxNodes: options?.partialGraphPolicy?.maxNodes,
maxRelations: options?.partialGraphPolicy?.maxRelations,
reservedRelations: options?.partialGraphPolicy?.reservedRelations
};
this.snapshotEnabled = false;
+4 -1
View File
@@ -62,7 +62,10 @@ export class ArbiterConfig {
this.arbiter.partialGraphPolicy = {
conflict_mode: conflictMode,
reducers
reducers,
maxNodes: next.maxNodes !== undefined ? next.maxNodes : current.maxNodes,
maxRelations: next.maxRelations !== undefined ? next.maxRelations : current.maxRelations,
reservedRelations: next.reservedRelations !== undefined ? next.reservedRelations : current.reservedRelations
};
return this.arbiter.partialGraphPolicy;
}
+40
View File
@@ -274,6 +274,46 @@ describe('Partial-graph overlay semantics (rigor)', () => {
}
});
it('POLICY LIMITS: maxNodes/maxRelations enforce and reservedRelations customizes', () => {
const four = [
{ src: 'u:0', relation: 'owner', dst: 'doc:0', possibility: 0.9 },
{ src: 'u:0', relation: 'owner', dst: 'doc:0', possibility: 0.8 },
{ src: 'u:0', relation: 'owner', dst: 'doc:0', possibility: 0.7 },
{ src: 'u:0', relation: 'owner', dst: 'doc:0', possibility: 0.6 }
];
const mk = (policy) => {
const a = new Arbiter(policy ? { partialGraphPolicy: policy } : {});
a.addNode('u:0', 'user');
a.addNode('doc:0', 'doc');
a.setRelationConfig('can_read', { type: 'direct', relation: 'owner' });
return a;
};
assert.throws(
() => mk({ maxRelations: 3 }).check('u:0', 'can_read', 'doc:0', { partialGraph: { relations: four } }),
/exceeds max relations/,
'constructor maxRelations enforces'
);
assert.throws(
() => mk({ maxNodes: 1 }).check('u:0', 'can_read', 'doc:0', {
partialGraph: { nodes: [{ key: 'g:a' }, { key: 'g:b' }], relations: four }
}),
/exceeds max nodes/,
'constructor maxNodes enforces'
);
const viaSetter = mk();
viaSetter.setPartialGraphPolicy({ maxRelations: 2 });
assert.throws(
() => viaSetter.check('u:0', 'can_read', 'doc:0', { partialGraph: { relations: four } }),
/exceeds max relations/,
'setter maxRelations enforces'
);
const customReserved = mk({ reservedRelations: ['owner'] });
const r = customReserved.check('u:0', 'can_read', 'doc:0', {
partialGraph: { relations: [{ src: 'u:0', relation: 'owner', dst: 'doc:0', possibility: 0.9 }] }
});
assert.equal(r.possibility, 0, 'custom reservedRelations skips owner');
});
it('PROPERTY CAMPAIGN: direct/chain overlay parity under persistent+partial mutations', async () => {
const result = await rigor.campaign(
[rigor.object('graph', makeWrapper, [