spindle · defeasible logic engine

Reason about rules that have exceptions.

Boolean logic assumes every rule holds always. The real world is full of defaults, overrides, and specificity. Spindle is a non-monotonic reasoning engine in Rust that handles conflict, priority, and withdrawal — and emits a proof tree you can show to an auditor.

Four rule types · superiority relations · Allen interval algebra Why-not · abduction · trust-weighted reasoning Native CLI · WASM for browser & Node

Classical logic can't model the world your decisions run on.

Every interesting rule system — policy, regulation, medical guidelines, pricing logic — is full of "normally X, but if Y." Prolog and Datalog can't express defeat. Rules engines like Drools bolt priority on top of boolean logic and leave you guessing why a conclusion held.

What you have today

A rules engine with hand-rolled priority ordering, no formal semantics, and no way to explain why conclusion A won out over conclusion B.

Prolog or Datalog that refuse to draw any conclusion when facts conflict, so you end up encoding defeat as negation-as-failure and crossing your fingers.

An LLM that produces a plausible-sounding answer with no derivation, no citations, and no guarantee it follows from the rules you actually wrote.

What spindle gives you

Defeasible logic with four rule types — facts, strict, defeasible, defeaters — and a superiority relation that resolves conflicts by your own policy.

Proof trees that cite every rule and premise used, rendered as JSON for machines or natural language for humans.

Temporal algebra and arithmetic built in — Allen's 13 interval relations, decimal money math, hypothetical what-if queries, and abduction.

Five queries against a theory of exceptions.

The classic penguin example: birds normally fly, but penguins don't. Click through a real spindle session.

01
Write the theory
Four rule types in SPL — facts, strict, defeasible, defeaters.
02
Derive conclusions
Forward chaining with explicit defeat status.
03
Ask why
Full proof tree citing every rule and premise.
04
Ask why-not
Missing facts that would change the answer.
05
Abduction
What facts would make the goal provable?
spindle · penguin.spl
; penguin.spl — birds normally fly, but penguins don't

(given bird)
(given penguin)

(normally r1 bird flies)
(normally r2 penguin (not flies))

(prefer r2 r1)   ; penguin-rule beats bird-rule
$ spindle reason penguin.spl

Conclusions:

  +D penguin           strictly provable
  +D bird              strictly provable
  +d ~flies            defeasibly provable
  -d flies             defeated by r2

# r1 fires, r2 fires, (prefer r2 r1) breaks the tie.
# Non-monotonic: add "penguin" and "flies" retracts.
$ spindle explain "~flies" penguin.spl

Explanation for +d ~flies
Proven defeasibly and not defeated by any conflicting rule.

Derivation:
  1. "~flies" was derived defeasibly
     Using defeasible rule: r2
     Prerequisites:
       1. "penguin" was established as a fact
          Using fact: f2

# Every step names the rule and the facts it depends on.
$ spindle why-not "flies" penguin.spl

flies is not provable:
  Would be derived by rule: r1
  Blocked by:
    - Rule r1: contradicted
      (Contradicted by r2)

# The engine names the rule that fired and the rule that blocked it.
$ spindle requires "flies" penguin.spl

Verified requirements for (flies):
   1. Add facts: {(flies)}

# Abduction: what would have to be true for the goal to hold?
# Here the penguin rule dominates, so the only route is asserting
# flies directly. Useful for diagnosing "why won't this derive?"

Three capabilities no production rules engine bundles together.

01 · Expressive

Four rule types, not one.

Model the real shape of policy: defaults that normally hold, strict rules that must hold, and defeaters that only block. Superiority encodes priority without ambiguity.

  • Facts, strict, defeasible, defeaters
  • Superiority relation over rules
  • Classical negation with explicit contradiction
  • Allen's 13 temporal interval relations
  • Decimal, integer, and float arithmetic
02 · Explainable

Proof trees, not black boxes.

Every conclusion carries a derivation. Render it as JSON for pipelines or natural language for humans. Trust-weighted reasoning surfaces how confident each step is.

  • JSON proof trees with provenance
  • Natural-language explanations
  • Why-not analysis for failed goals
  • Trust weights on rules and facts
  • Conflict detection and reporting
03 · Embeddable

Native speed, WASM reach.

Ship the same engine to a CLI, a server, a browser, or a Node runtime. SPL (Spindle Lisp) is the source DSL; theories are files you can diff, review, and version.

  • Pure Rust, single static binary
  • WASM target for browser & Node
  • S-expression DSL (SPL)
  • CLI, library, and REPL
  • LGPL-3.0-or-later

Write a theory. Run the engine. Read the proof.

Spindle compiles SPL into an internal representation, performs forward chaining with defeasible derivation, and emits a labelled proof graph. Labels are the classic DL four: +Δ, −Δ, +∂, −∂.

┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ theory.spl │ │ spindle engine │ │ proof graph │ │ │ │ │ │ │ │ facts │─────►│ parse │─────►│ +Δ strict │ │ strict rules │ │ build IR │ │ +∂ defeasibly │ │ defeasible │ │ forward chain │ │ −Δ / −∂ blocked │ │ defeaters │ │ apply prefer │ │ │ │ prefer rel │ │ derive labels │ │ JSON · NL · DOT │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ ▼ ┌──────────────────────────────┐ │ explain · why-not · │ │ requires · reason · stats │ └──────────────────────────────┘

Non-monotonic by design

Adding a fact can retract a conclusion. That's the point: it's how real policy and regulation work.

Decidable, not Turing-complete

Defeasible logic terminates. No infinite recursion, no halting problem — just bounded derivation.

Temporal reasoning first-class

Allen's 13 relations (before, meets, overlaps, during, …) are primitives, not library macros.

Arithmetic that won't lose cents

Three numeric types (Integer, Decimal, Float) with explicit coercions. Decimal math is exact.

SPL: a small, honest DSL

S-expressions. No hidden macros, no operator precedence gotchas. Diffable and review-friendly.

Ship to the browser

WASM build runs in Node and any modern browser. Reason in the page, not on a server.

Frequently asked questions.

How is this different from Prolog / Datalog?
Prolog and Datalog are monotonic — new facts never invalidate old conclusions. Spindle is non-monotonic: adding "tweety is a penguin" retracts "tweety flies." Defeasible logic has a formal semantics for this; Prolog approximates it with negation-as-failure and cut, which have no declarative reading.
How is this different from Drools / a production rules engine?
Drools lets you write priority as an integer on each rule. Spindle uses an explicit superiority relation with a formal semantics, and emits a proof tree you can show an auditor. Drools is imperative and effectful; spindle is declarative and deterministic.
Can I use this from a language that isn't Rust?
Yes. The engine compiles to WASM, so it runs anywhere WASM runs — browser, Node, Deno, Wasmtime, Cloudflare Workers. Theories are text files (SPL), so you can shell out to the CLI from any language.
What's a defeater, and when do I need one?
A defeater is a rule that blocks a conclusion without asserting the opposite. Useful when you know "in this case the default shouldn't apply" but aren't ready to commit to the negation. Example: "if the customer is disputing, don't auto-charge" — without asserting anything about whether to refund.
Is this research code or can I ship it?
Ship it. Spindle has proof-tree provenance, decimal arithmetic for money, stable SPL syntax, a CLI, and a WASM target. It's LGPL-3.0 so you can link it from proprietary software. Formal semantics follow Antoniou et al.'s defeasible logic.
Where's the source?
Open source, LGPL-3.0-or-later. codeberg.org/anuna/spindle-rust — install with cargo or build from source.

Give your rules the reasoning they deserve.

Install the CLI, write a theory, ask for a proof. Ten minutes from zero to your first defeasible derivation.

install: cargo install --git https://codeberg.org/anuna/spindle-rust spindle-cli