feat: chain condition steps — defeasible/logical evidence as final chain hop
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:
@@ -155,6 +155,7 @@ export class RuleGenerator {
|
||||
for (const step of rule.steps) {
|
||||
if (typeof step === 'string') targetSet.add(step);
|
||||
else if (step && typeof step.relation === 'string') targetSet.add(step.relation);
|
||||
else if (step && step.rule) collect(step.rule, targetSet);
|
||||
}
|
||||
}
|
||||
if (rule.type === 'relational_comparator') {
|
||||
@@ -1070,13 +1071,18 @@ export class RuleGenerator {
|
||||
* - chain evidence → splice its steps into this chain (flattening)
|
||||
* (a step that is itself a sub-path becomes its steps, preserving the
|
||||
* linear source→…→object traversal);
|
||||
* - anything else (defeasible/logical/comparator) → compile error: such a
|
||||
* step is a condition, not an edge traversal, and cannot lower to a flat
|
||||
* chain step.
|
||||
* - logical / defeasible / comparator evidence → only expressible as a
|
||||
* FINAL condition-gated step (the object is known, so the engine can
|
||||
* verify the condition at (intermediate, object) instead of traversing
|
||||
* an edge). Emitted as a `{ rule: <config> }` step the ChainRule
|
||||
* evaluates as a condition hop. Non-final such steps are a compile
|
||||
* error: a condition cannot discover intermediate nodes.
|
||||
*/
|
||||
_expandChainSteps(steps, stack) {
|
||||
const out = [];
|
||||
for (const step of steps) {
|
||||
for (let idx = 0; idx < steps.length; idx++) {
|
||||
const step = steps[idx];
|
||||
const isLast = idx === steps.length - 1;
|
||||
const stepName = typeof step === 'string' ? step : step.relation;
|
||||
if (stepName && this.evidenceNames.has(stepName)) {
|
||||
if (stack.has(stepName)) {
|
||||
@@ -1099,8 +1105,14 @@ export class RuleGenerator {
|
||||
out.push(...this._expandChainSteps(resolved.steps, refStack));
|
||||
continue;
|
||||
}
|
||||
if (isLast) {
|
||||
// Condition-gated final hop: inline the evidence's config as a
|
||||
// rule step the engine evaluates at (intermediate, object).
|
||||
out.push({ rule: this._deepCloneRule(resolved), conditionStep: true });
|
||||
continue;
|
||||
}
|
||||
this.errors.push(`Chain step '${stepName}' references an evidence with type '${resolved.type || 'logical'}'. ` +
|
||||
'Chain steps can only reference facts, direct evidence, or chain evidence.');
|
||||
'Only the final chain step may reference a defeasible/logical evidence (a condition-gated hop); intermediate steps must be edge traversals.');
|
||||
out.push(step);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,11 @@ export class DSLRuntime {
|
||||
if (rule.computedRelation) deps.add(rule.computedRelation);
|
||||
}
|
||||
if (rule.type === 'chain' && Array.isArray(rule.steps)) {
|
||||
for (const s of rule.steps) deps.add(typeof s === 'string' ? s : s.relation);
|
||||
for (const s of rule.steps) {
|
||||
if (typeof s === 'string') deps.add(s);
|
||||
else if (s && s.relation) deps.add(s.relation);
|
||||
else if (s && s.rule) collect(s.rule);
|
||||
}
|
||||
}
|
||||
if (rule.type === 'parent' && rule.parentRelation) deps.add(rule.parentRelation);
|
||||
if (rule.type === 'multi_hop' && rule.relation) deps.add(rule.relation);
|
||||
|
||||
Reference in New Issue
Block a user