- LogicalOperators normal mode now emits top-level reason codes
(never_rule_triggered / requirements_not_met / defeated_by_unless) when a
defeasible rule resolves to 0, instead of degrading to no_matching_rule.
- RuleEvaluator honors rule._subjectAsObject: unary DSL predicate calls inside
binary evidence (banned(user) within can_open(user, doc)) check the relation
on the subject itself, matching the unary fact's self-edge.
- AuthorizationChecker reason whitelist now preserves the defeasible reason
codes so the checker reports why a defeasible rule denied.
The Evidence DSL (ADR-000) compiles a natural DSL into core rule
configurations — it is a separate concern from the engine. The AST had
zero runtime coupling to the core (DSLCompiler takes the arbiter as a
duck-typed argument; ip-utils were the only shared code, now local to
the DSL package). This extraction removes the DSL surface from the core
artifact entirely:
- src/ast/ (748K, ~60 files) moved to @arbiter/evidence-dsl@1.0.0
- ip-utils moved with it (only the DSL consumed them)
- generate-parser script + peggy devDep moved to the DSL package
- the 8 DSL-consuming tests now import from @arbiter/evidence-dsl
(deep-path exports: DSLCompiler, parser/*, generator/*, validation/*,
interpreter/*)
- package.json gains the devDependency, drops build:ast/generate:parser
Tarball: AST-free. Rigor 251/251, full suite 838/776/0.
The prod-gating measurement (complex-query-bench: tuple-to-userset,
chains, defeasible, comparators, OWA, nested fusion, mixed unions over
cold traffic with binary parity) was committed but never ran in CI. The
benchmark job now runs it alongside the possibilistic baseline compare,
uploads the results artifact, and surfaces both in the run summary.
Also: refreshed the committed .rigor-baseline.json (engine unchanged
since the tag; the runner's canonical baseline lives in its artifact
store), and added npm run benchmark:complex-query for local runs.
Prod gating is dominated by rule-based queries, not direct relations.
complex-query-bench.js measures cold-traffic latency (distinct
subject/object per sample, no cache reuse) across seven complex policy
shapes — tuple-to-userset, 2-hop chain, defeasible exclusion, ABAC
relational comparator, OWA union, nested comparator + OWA fusion, and a
mixed 10-rule union — for both evaluation paths, and enforces binary/
normal decision parity on every query.
At 25k and 100k nodes: binary wins every scenario (1.15x-2.0x median
speedup), p99 stays sub-0.05ms, and parity mismatches are zero across
all scenarios. Binary's early exit wins where a strong rule exists; the
earlier direct-relation 'binary slower' observation was a cache-hit
artifact (normal serves repeat queries from the rule result cache,
binary correctly does not, since thresholds are per-call options).
Note: report median, not avg — GC outliers inflate the mean (avg > p95
observed on two rows).
README: purpose-first (possibility not boolean, caller owns evidence/time),
install, verified quick start, concepts (result shape, overlays, temporal
context), API table, development commands, design notes.
benchmark: scripts/benchmark.js on @tenere/benchmark-lib — eight contours
(direct/union/denied/meta/overlay/binary checks, snapshot build/restore),
committed .rigor-baseline.json, exit 1 on high-severity regressions.
CI: benchmark job compares on push (continue-on-error), re-saves baseline
and uploads it as an artifact on tags; publish now depends on benchmark
passing as well as test.
- @tenere/pltc-core switches from a machine-local file: dependency to the
published ^0.6.3 from the Tenere npm registry (the file: path does not
exist in a fresh checkout; lockfile regenerated to the tarball).
- .npmrc routes @tenere and @arbiter scopes alongside @rigor so registry
installs and the eventual publish resolve against the hub.
- package.json gains a files whitelist (src/ only) so tests, benchmarks,
and scratch directories never ship in the tarball.
- .gitea/workflows/ci.yaml: node 22, full suite + rigor campaigns on push/
PR, npm publish on v* tags to the Arbiter org registry.