Skip to content

marcelxv/rune

Repository files navigation

Rune

Deterministic rule engine for agent systems

License Version Status

Rune is a language for encoding decision logic as deterministic finite state machines. It is designed to sit between LLM reasoning and real-world actions — guaranteeing that the same inputs always produce the same outcome.

Rune policies take typed inputs and always terminate. There are no loops, no side effects, no ambiguity.

Why Rune

LLMs reason probabilistically. Real-world actions (approvals, rejections, escalations) must be deterministic. Rune is the layer that enforces this — a tiny, auditable runtime where business rules are explicit, testable, and version-controlled.

Features

  • Determinism — same inputs, same terminal. Always.
  • Type safetynumber, boolean, string, and enum inputs
  • Lint + type checker — catches structural and semantic errors before runtime
  • Spec format — define policies as JSON (.runespec.json) and compile to .rune
  • LLM extraction — extract policies from natural language documents using an LLM

Getting Started

npm install
npm run build

Run the CLI:

npm run rune -- <command> [options]

Or directly:

node packages/cli/dist/index.js <command> [options]

Write a Policy

Create policy.rune:

version "0.1"

input risk_score: number
input has_docs: boolean

initial review

state review {
  when risk_score > 80 && has_docs == false then reject
  when risk_score <= 30 then approve
  else manual_check
}

state manual_check {
  when has_docs == true then approve
  else reject
}

terminal approve
terminal reject

CLI Commands

Command Description
lint <file> Validate a .rune policy file
eval <file> Evaluate a policy against a JSON input
spec:check <file> Validate a .runespec.json schema
spec:lint <file> Semantic lint of a .runespec.json
spec:compile <file> Compile a .runespec.json to .rune
compile [file] Compile a document into a policy using an LLM

Lint

npm run rune -- lint policy.rune

Evaluate

npm run rune -- eval policy.rune --input input.json

Output:

Terminal   approve
Path       review -> manual_check -> approve
Steps      3

Compile (LLM)

npm run rune -- compile recipe.txt --provider openai --api-key $OPENAI_API_KEY --out policy.rune
npm run rune -- compile --text "approve if score < 50, else reject" --provider deepseek --api-key $DEEPSEEK_API_KEY

Use --draft-dir .rune/drafts/my-policy to write a full analysis package:

  • policy.rune — the extracted policy
  • report.md — human-readable extraction report
  • warnings.json — structured warnings
  • trace.json — document → rule mapping

Using Rune in Your Project

Rune is designed to embed cleanly inside any existing repo. Add it as a dependency, drop your policies alongside your code, and use the CLI to compile, lint, and test them.

Install

npm install @rune/core @rune/cli

Recommended layout

<your-project>/
  policies/             ← versioned .rune files (check these in)
    refund.rune
    access-control.rune
    tests/              ← gsi-generated test cases (check these in too)
      refund.test.json
      access-control.test.json

  recipes/              ← source documents that policies are compiled from
    refund.txt
    access-control.md

  .rune/                ← tool output — add to .gitignore
    drafts/
      refund/           ← full draft package from `rune compile`

Add .rune/ to your .gitignore:

# .gitignore
.rune/

Typical workflow

1. Write or compile a policy

# From a document (interactive wizard):
rune compile recipes/refund.txt

# The wizard defaults draft output to .rune/drafts/refund/
# Move the finished policy to policies/ when satisfied:
cp .rune/drafts/refund/policy.rune policies/refund.rune

2. Lint

rune lint policies/refund.rune

3. Generate test cases

rune gsi policies/refund.rune
# → writes policies/tests/refund.test.json automatically

4. Evaluate

rune eval policies/refund.rune --input '{"amount": 450, "customer_tier": "gold", "has_receipt": true}'

Runtime usage (@rune/core)

import { parse, evaluate } from "@rune/core";
import { readFileSync } from "node:fs";

const policy = parse(readFileSync("policies/refund.rune", "utf-8"));
const result = evaluate(policy, { amount: 450, customer_tier: "gold", has_receipt: true });
// result.terminal → "approve" | "reject" | "escalate"

Project Layout

The policies/, recipes/, and .rune/ directories follow established conventions:

  • .rune/ mirrors .git/, .next/, .turbo/ — tool artifacts live in a dot-dir, gitignored
  • policies/tests/ mirrors Jest __tests__/, dbt tests/ — test cases co-located with what they test
  • recipes/ mirrors prisma/, migrations/ — source inputs that produce compiled artifacts

Repository Structure

packages/
  core/         Parser, validator, type checker, evaluator, spec compiler
  cli/          Command-line interface
  ingestion/    LLM-based policy extraction
test-suite/     Integration tests and fixtures
spec/           Language specification

Documentation

See HANDBOOK.md for the full language reference, grammar, error codes, and the spec format.

Origin

Rune was extracted from production work on an AI-driven document validation platform processing ~1,000 documents daily. It is the layer that makes the final decision deterministic inside an otherwise probabilistic agent system.

License

MIT — LICENSE


Built by Marcel Scognamiglio

About

Deterministic rule engine for agent systems

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors