Skip to content
docs
Arxo ↗

Packages and cases

For LLMs4 sections

One session, two folders: a canon package holds the rules, a case package holds the situation. The canon below is a parcel tariff — up to 2 kg the fee is 500, above that 500 plus 150 for every kilogram over 2. Every terminal ran; every file is shown as written.

law init with the package template creates law.toml and source.law:

Terminal
$ law init parcel-tariff --name demo.parcel_tariff --template package
parcel-tariff: law.toml, source.law

The rules go into source.law:

parcel-tariff/source.law
language "law.core" version "0.2";
package demo.parcel_tariff version "0.1.0";
namespace "urn:law:demo:parcel_tariff";
pub entity Shipment { label en unofficial "Shipment"; }
pub relation weight_kg(s: Shipment, kg: Decimal) kind empirical {
label en unofficial "weight of the shipment, kg";
}
pub relation fee(s: Shipment, amount: Decimal) kind institutional {
label en unofficial "postage under the tariff";
}
rule BaseFee(s: Shipment, kg: Decimal) strict {
label en official "up to and including 2 kg: base fee 500";
when weight_kg(s, kg);
when kg <= 2.0;
then fee(s, 500.0);
}
rule HeavyFee(s: Shipment, kg: Decimal) strict {
label en official "over 2 kg: 500 plus 150 for each kilogram above 2";
when weight_kg(s, kg);
when kg > 2.0;
then fee(s, 500.0 + (kg - 2.0) * 150.0);
}

Expectations are written from the tariff text before the run: 5 kg goes to 500 + 3 × 150 = 950; exactly 2 kg stays at 500. tests/tariff.lawtest:

parcel-tariff/tests/tariff.lawtest
language "law.core" version "0.2";
package demo.parcel_tariff version "0.1.0";
namespace "urn:law:demo:parcel_tariff";
test "urn:query:demo-parcel-tariff-t01-heavy" {
given {
context { legal_time @2026-09-25; decision_time @2026-09-25T00:00:00Z; knowledge_time @2026-09-25T00:00:00Z; timezone "UTC"; }
assert "w": weight_kg(entity_ref("urn:case:box-1"), 5.0) { origin case_input; }
}
evaluate truth(fee(entity_ref("urn:case:box-1"), 950.0));
expect truth_status == TRUE_ONLY;
expect evaluation_status == COMPUTED;
}
test "urn:query:demo-parcel-tariff-t02-base" {
given {
context { legal_time @2026-09-25; decision_time @2026-09-25T00:00:00Z; knowledge_time @2026-09-25T00:00:00Z; timezone "UTC"; }
assert "w": weight_kg(entity_ref("urn:case:box-2"), 2.0) { origin case_input; }
}
evaluate truth(fee(entity_ref("urn:case:box-2"), 500.0));
expect truth_status == TRUE_ONLY;
expect evaluation_status == COMPUTED;
}

And law.toml gains the suite stanza:

parcel-tariff/law.toml.append.toml
[[tests]]
family = "parcel_tariff"
suites = ["tests/tariff.lawtest"]
world = ["demo.parcel_tariff"]

The first draft carries a typo: line 23 of source.law calls the relation weight_kgs. The static check reports it with a line, a column and a stable code:

Terminal
$ law engine check .
./source.law:23:10: error LDC-E2102: predicate "weight_kgs" is not declared as a relation in this file
(exit 1)

The code LDC-E2102 names the fault — a predicate with no declaration — so an editor can react to the code rather than the wording. With the name corrected the check passes:

Terminal
$ law engine check .
check OK: .

law engine lock writes law.lock:

Terminal
$ law engine lock . --write
written ./law.lock

And law test runs the declared scenarios:

Terminal
$ law test
law test demo.parcel_tariff: world demo.parcel_tariff
ok [parcel_tariff] tests/tariff.lawtest / urn:query:demo-parcel-tariff-t01-heavy
ok [parcel_tariff] tests/tariff.lawtest / urn:query:demo-parcel-tariff-t02-base
total: 2 checked, 2 passed, 0 failed, 0 not run; code 0

law pack puts the canon into one portable file:

Terminal
$ law pack --out ../tariff.arxo
demo.parcel_tariff → ../tariff.arxo: 6 files, 5 objects (12 KiB → 7 KiB after dedup → 6 KiB in the archive)
containerHash sha256:829566fe4347…; capabilities: execute, rebuild, test

A case package holds your situation; the canon arrives as a pinned dependency. A second folder, a second init:

Terminal
$ cd .. && law init my-case
my-case: case package my_case, law.core language 0.2
created: law.toml, package.law, main.lawcase, law.lock
case Main declared in main.lawcase and registered in law.toml; set the time axis by explicit input
next: law add <package> → law install → law ask

law add pins the packed canon with no network:

Terminal
$ cd my-case && law add ../tariff.arxo
demo.parcel_tariff@0.1.0 ← ../tariff.arxo (containerHash sha256:829566fe4347…), registries: file
file ← /private<tmp>/my-case/.law/arxo-registry-77540/file (--registry)
+ demo.parcel_tariff 0.1.0
exports (3): Shipment, fee, weight_kg
changed: .law/transport.json
changed: deps/demo.parcel_tariff.lawir.json
changed: law.lock
changed: law.toml
changed: package.law

The case file sets the date axis and asserts one fact — a 5 kg shipment:

my-case/main.lawcase
language "law.core" version "0.2";
package my_case version "0.1.0";
namespace "urn:law:my_case";
case Main {
context {
legal_time @2026-10-01;
knowledge_time @2026-10-01T12:00:00Z;
decision_time @2026-10-01T12:00:00Z;
timezone "UTC";
}
assert demo.parcel_tariff::weight_kg(entity_ref("urn:case:box-1"), 5.0) { origin case_input; }
}

The question asks the fee for that shipment:

my-case/query.txt
evaluate truth(demo.parcel_tariff::fee(entity_ref("urn:case:box-1"), 950.0));

law ask saves the request with the full evaluation document; piped through jq the summary reads as a status with the result hash:

Terminal
$ law ask --query "$Q" --query-id fee --format brief --out runs/fee | jq -r '.results[0].status, .resultHash'
TRUE_ONLY
sha256:0290d550984a…

law eval recomputes the saved run and reaches the same hash:

Terminal
$ law eval runs/fee | jq -r .resultHash
sha256:0290d550984a…

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

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