feat: fix lowering, DSLRuntime wrapper, rigor oracle + rejection campaigns
Lowering fixes (validate/lower/compile into known-correct core structures):
- tuple_to_userset: structural classification by object-side predicate
(owner(*g, doc) { member_of(user, g) } -> tuple_to_userset with direction
'in'/'out'); the old heuristic routed every outer-wildcard to chain.
- relational_comparator: operands now lower to real direct-rule configs
(evaluateFrom derived from evidence param positions; expectedValue for
literal args) instead of raw AST nodes the engine could not evaluate.
- defeasible: multi-level bodies (NEVER/REQUIRES/ALWAYS/WHEN/UNLESS) merge
into one five-level rule instead of ANDed level-only rules that always
resolved 0; nested PatternMatches flatten to N-step chains; unary predicate
calls mark _subjectAsObject (subject-as-object semantics).
- validation: reject duplicate fact/evidence definitions.
DSLRuntime (higher-order DSL+Core wrapper):
- typed addNode/updateNodeData/addRelation/updateRelation against the DSL
schema (known types, relation params, field types, value-carrying facts);
- check() derives the evidence's injectable partial-graph requirements,
retrieves missing facts through caller data callbacks, injects them, and
delegates, returning requiredFacts/providedFacts/missingFacts.
js-rigor campaigns:
- generative oracle: generate legal DSL per construct and compare every
verdict against an independent hand-computed oracle (8 constructs x P grid)
plus an exhaustive deterministic sweep;
- illegal mutations: one-flaw perturbations of a valid program must be
reliably rejected (duplicate evidence/fact, arity/type mismatches, reserved
built-ins, malformed syntax), with a control that must compile.
Depends on @arbiter/core@^1.0.2 (reason codes + _subjectAsObject).
This commit is contained in:
@@ -144,6 +144,7 @@ function validateDefinitions(program, tables, errors, warnings, source) {
|
||||
}
|
||||
|
||||
function validateFacts(program, tables, errors, warnings, source) {
|
||||
const seen = new Map();
|
||||
for (const fact of program.facts || []) {
|
||||
if (tables.builtins?.facts?.has(fact.name)) {
|
||||
errors.push(createError({
|
||||
@@ -154,6 +155,16 @@ function validateFacts(program, tables, errors, warnings, source) {
|
||||
context: formatContext(source, findLocation(source, fact.name))
|
||||
}));
|
||||
}
|
||||
if (seen.has(fact.name)) {
|
||||
errors.push(createError({
|
||||
message: `Duplicate fact definition '${fact.name}'.`,
|
||||
rule: 'Each fact name must be unique within a program.',
|
||||
fix: 'Rename one of the fact definitions to a unique name.',
|
||||
location: findLocation(source, `fact ${fact.name}`),
|
||||
context: formatContext(source, findLocation(source, fact.name))
|
||||
}));
|
||||
}
|
||||
seen.set(fact.name, fact);
|
||||
const arity = fact.params ? fact.params.length : 0;
|
||||
if (!fact.params || arity === 0) {
|
||||
warnings.push(createError({
|
||||
@@ -285,6 +296,7 @@ function validateMeasures(program, tables, errors, warnings, source) {
|
||||
}
|
||||
|
||||
function validateEvidence(program, tables, errors, warnings, source) {
|
||||
const seen = new Map();
|
||||
for (const ev of program.evidence || []) {
|
||||
if (tables.builtins?.evidence?.has(ev.name)) {
|
||||
errors.push(createError({
|
||||
@@ -295,6 +307,16 @@ function validateEvidence(program, tables, errors, warnings, source) {
|
||||
context: formatContext(source, findLocation(source, ev.name))
|
||||
}));
|
||||
}
|
||||
if (seen.has(ev.name)) {
|
||||
errors.push(createError({
|
||||
message: `Duplicate evidence definition '${ev.name}'.`,
|
||||
rule: 'Each evidence name must be unique within a program.',
|
||||
fix: 'Rename one of the evidence definitions to a unique name.',
|
||||
location: findLocation(source, `evidence ${ev.name}`),
|
||||
context: formatContext(source, findLocation(source, ev.name))
|
||||
}));
|
||||
}
|
||||
seen.set(ev.name, ev);
|
||||
const returnType = ev.provides || DEFAULT_EVIDENCE_RETURN;
|
||||
if (returnType !== DEFAULT_EVIDENCE_RETURN && !isTypeKnown(returnType, tables)) {
|
||||
errors.push(createError({
|
||||
|
||||
Reference in New Issue
Block a user