feat: chain condition steps — defeasible/logical evidence as final chain hop
CI / publish (push) Successful in 9s
CI / test (push) Successful in 18s

A chain's FINAL (object-side) step may now reference a defeasible/logical
evidence. The compiler lowers it to a condition step
({ rule: <config>, conditionStep: true }) that the engine verifies at
(intermediate, object) instead of traversing an edge. Requires
@arbiter/core@^1.0.3 (ChainRule condition-step support).

- _expandChainSteps: a logical/defeasible/comparator evidence is expressible
  as a final condition step; non-final such steps remain a compile error
  (a condition cannot discover intermediate nodes).
- Dependency collection (generator + DSLRuntime) descends into condition-step
  rule configs, so partial-graph requirements reach through them.

Tests: ChainConditionStep (defeasible + ALWAYS steps, independent checkability,
parallel aggregation), oracle campaign chain_condition_step construct
(oracle = min(pm, pv*(1-pb))), DSLRuntime transitive required facts through a
condition step.
This commit is contained in:
John Dvorak
2026-08-03 12:08:54 -07:00
parent 2dc478f5a3
commit 351551af0f
9 changed files with 178 additions and 31 deletions
+8 -4
View File
@@ -81,14 +81,18 @@ describe('Chain step composition', () => {
assert.equal(arb.check('u:1', 'can_via', 'doc:9').possibility, 0.6);
});
it('rejects a defeasible/logical evidence as a chain step', () => {
const { result } = compile(`
it('lowers a logical evidence FINAL step to a condition step', () => {
const { arb, result } = compile(`
${DEFS}
evidence gated(group: Group, doc: Doc) { WHEN can_view(group, doc) UNLESS banned(group) }
evidence can_via(user: Employee, doc: Doc) { member_of(user, *g) { gated(g, doc) } }
`);
assert.equal(result.success, false);
assert.ok(result.errors.some(e => /Chain step 'gated'/.test(e)), JSON.stringify(result.errors));
assert.ok(result.success, JSON.stringify(result.errors));
// final-step logical evidence → condition step (verified at the object)
const steps = arb.relationConfigs.get('can_via').steps;
assert.equal(steps[0], 'member_of');
assert.equal(steps[1].conditionStep, true);
assert.equal(steps[1].rule.type, 'logical');
});
it('rejects a mutual cycle through chain steps', () => {