Skip to content

Your first rule

The task: the town of Northbridge issues residential parking permits. An applicant is eligible when two things hold — they are a resident, and they have a vehicle registered at their address. We will write that rule, give it one applicant, and ask whether she is eligible.

This page is a complete package. Every law block below is compiled by the checks that publish this site, so the code you see is the code that ran.

Open this package in the playground → — edit the rules and the scenarios of this page and run them in your browser.

A package starts with three lines: the language line, the package name with its version, and a namespace that makes every name in the package globally addressable.

language "law.core" version "0.2";
package demo.parking version "0.1.0";
namespace "urn:law:demo:parking";

Then the vocabulary. An entity is a kind of thing the rules talk about; a relation is a statement about such things. kind institutional says the statement exists because some rule or record says so, as opposed to a measured fact.

entity Applicant;
relation resident(a: Applicant) kind institutional;
relation vehicle_registered(a: Applicant) kind institutional;
relation permit_eligible(a: Applicant) kind institutional;

The rule. for introduces a typed variable; when lists what must be established; then is what follows. strict means the rule has no exceptions — nothing can defeat its conclusion.

rule PermitEligibility strict {
for a: Applicant;
when resident(a) and vehicle_registered(a);
then permit_eligible(a);
}

A scenario lives in a separate file with the extension .lawtest. It repeats the same three header lines, then states what is given, what is asked, and what is expected. The context block fixes the dates the question is asked at; the two assert lines are the facts of the case.

test "a resident with a registered vehicle is eligible" {
given {
context { legal_time @2026-03-01; decision_time @2026-03-01T09:00:00Z; knowledge_time @2026-03-01T09:00:00Z; timezone "UTC"; }
assert resident(entity_ref("urn:demo:parking:ann")) { id "ann-resident"; origin case_input; }
assert vehicle_registered(entity_ref("urn:demo:parking:ann")) { id "ann-vehicle"; origin case_input; }
}
evaluate truth(permit_eligible(entity_ref("urn:demo:parking:ann")));
expect truth_status == TRUE_ONLY;
expect evaluation_status == COMPUTED;
}

evaluate truth(…) asks whether a statement is established. The answer has two parts: evaluation_status says whether the engine could compute at all (COMPUTED), and truth_status is the answer itself — here TRUE_ONLY: established, with nothing against it.

Save the law blocks above as two files: the package as parking.law, and the test block, preceded by the same three header lines, as tests/eligibility.lawtest. Then check the package and run the scenario against it (law is the Arxo command-line tool; inside a checkout of the repository it is ./law):

Terminal window
law engine check parking.law
law engine test tests/eligibility.lawtest --program parking.law

Expected output:

check OK: parking.law
test PASS: a resident with a registered vehicle is eligible

followed by a summary line with the count of scenarios that passed.

Delete the vehicle_registered line from given and run the scenario again. The rule no longer fires, and the test fails:

test FAIL: a resident with a registered vehicle is eligible
truth_status == TRUE_ONLY: в документе NEITHER

The answer is NEITHER — neither established nor refuted. Nothing in the case says Ann has no registered vehicle; the case is silent, and Arxo says so instead of guessing. The next page, Facts and questions, shows the other kinds of questions you can ask; Missing and conflicting facts explains the four possible answers.

Documentation for Arxo. Writings — blog.arxo.io.

Anonymous visit counts on stats.arxo.io, no cookies.