feat: sources wired as recency-gated injectables; transitive closure, NOT scoping, recompile-scope uninstall; targeted parse errors
BEHAVES AS transitive now emits bounded multi_hop configs (direct checks and evidence references), fixing a silent no-op. NOT builds keep _subjectAsObject scoping so unary predicates negate the right node, and value-typed evidence objects gate by exact edge value. Recompiling a scope uninstalls its stale relation configs (compileMultiple coexistence preserved). Sources become injectable relations honored by requiredFacts with a within-X recency gate. Duplicate definition fields and three common declaration mistakes (within on a fact, two BEHAVES clauses, limit on a non-pattern body) now produce targeted errors. Provider edges referencing unknown nodes are warned and dropped.
This commit is contained in:
@@ -165,4 +165,33 @@ describe('DSLRuntime extended', () => {
|
||||
assert.equal(missed.possibility, 0);
|
||||
assert.deepEqual(missed.missingFacts, [{ relation: 'owns', reason: 'no_provider' }]);
|
||||
});
|
||||
|
||||
it('recompiling a scope uninstalls its stale relation configs', async () => {
|
||||
const rt = new DSLRuntime(new Arbiter());
|
||||
rt.compile(`
|
||||
definition Employee { id: string? }
|
||||
definition Doc { id: string? }
|
||||
fact owns(user: Employee, doc: Doc)
|
||||
evidence can_read(user: Employee, doc: Doc) { owns(user, doc) }
|
||||
`, 'rt-stale');
|
||||
rt.addNode('u:1', 'Employee', {});
|
||||
rt.addNode('doc:9', 'Doc', {});
|
||||
rt.addRelation('u:1', 'owns', 'doc:9', { possibility: 1.0 });
|
||||
assert.equal(rt.arbiter.check('u:1', 'can_read', 'doc:9').possibility, 1.0);
|
||||
|
||||
// Recompile the SAME scope with a different program: can_read/owns are no
|
||||
// longer declared and must not keep granting. A second scope's relations
|
||||
// would be unaffected (compileMultiple coexistence).
|
||||
rt.compile(`
|
||||
definition Employee { id: string? }
|
||||
definition Doc { id: string? }
|
||||
fact shares(user: Employee, doc: Doc)
|
||||
evidence can_share(user: Employee, doc: Doc) { shares(user, doc) }
|
||||
`, 'rt-stale');
|
||||
const stale = rt.arbiter.check('u:1', 'can_read', 'doc:9');
|
||||
assert.equal(stale.possibility, 0, 'revoked can_read must not keep granting');
|
||||
assert.ok(!rt.arbiter.relationConfigs.has('can_read'));
|
||||
assert.ok(!rt.arbiter.relationConfigs.has('owns'));
|
||||
assert.ok(rt.arbiter.relationConfigs.has('can_share'));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user