docs← Back to article

Markdown for LLMs

Packages and cases

The source Markdown for this article. Copy it into your assistant or download it as a text file.

Download this articlePlain text ↗
# Packages and cases

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.

## A canon package

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

```text title="Terminal"
$ law init parcel-tariff --name demo.parcel_tariff --template package
parcel-tariff: law.toml, source.law
```

The rules go into `source.law`:

```law title="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`:

```law title="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:

```toml title="parcel-tariff/law.toml.append.toml"

[[tests]]
family = "parcel_tariff"
suites = ["tests/tariff.lawtest"]
world = ["demo.parcel_tariff"]
```

## Check, fix, test

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:

```text title="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](/diagnostics/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:

```text title="Terminal"
$ law engine check .
check OK: .
```

`law engine lock` writes `law.lock`:

```text title="Terminal"
$ law engine lock . --write
written ./law.lock
```

And `law test` runs the declared scenarios:

```text title="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
```

## A case under it

`law pack` puts the canon into one portable file:

```text title="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:

```text title="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:

```text title="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:

```law title="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:

```text title="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:

```text title="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:

```text title="Terminal"
$ law eval runs/fee | jq -r .resultHash
sha256:0290d550984a…
```

## Where next

- To write rules of your own: the [Language](/language/) topic, from a
  first rule to a package.
- To read the saved document field by field: [How to read an
  answer](/guide/reading-an-answer/) and the [Decision
  protocol](/protocols/decision-protocol/).
- To see what a question sends: the [Fact
  protocol](/protocols/fact-protocol/).
- When the check fails on another code: [Diagnostics](/diagnostics/).