Skip to content
docs
Arxo ↗

Several packages and a world

For LLMs7 sections

The archive does not set the copy page rate — the city council adopts it by its decision. So the archive holds two acts: its own rules and someone else’s decision, and the archive norm must read the magnitude from the neighbouring package rather than rewrite it at home. A visible part of the corpus is arranged so: 57 corpus packages declare [dependencies], and the budget act with the MRP is the most frequent dependency (measured 05.09.2026). This page is the first with two programs instead of one, and the first checked not on the page itself but on a package in a tree: the practicum tree holds tutorial.city and tutorial.archive with manifests, a pinned law.lock, and scenarios, while the page must match it in forms and execute on its dependencies.

Arxo Law
language "law.core" version "0.2";
package tutorial.archive version "0.15.0";
namespace "urn:law:tutorial:archive";
import tutorial.city version "0.1.0";
entity Person;
entity CopyOrder;
relation billing_year(p: Person, year: Integer) kind empirical { key(p); }
relation copy_order(o: CopyOrder, p: Person, pages: Integer) kind empirical { key(o); }
relation copy_fee(o: CopyOrder, amount: Money) kind institutional { key(o); }

The council decision is a separate tutorial.city package with its own name, namespace, and manifest. Everything the archive may read from it is marked pub: an unmarked relation does not exist from outside, and a reference to it is a refusal, not silence.

Arxo Law
language "law.core" version "0.2";
package tutorial.city version "0.1.0";
namespace "urn:law:tutorial:city";
pub relation base_page_rate(year: Integer, amount: Money) kind institutional;
pub relation late_fee_unit(year: Integer, amount: Money) kind institutional;
assert base_page_rate(2026, 150 KZT) {
id "city-page-rate-2026";
origin source_asserted;
}

The rate fact lives in the council package itself with the source_asserted provenance: it is the act’s norm, not case input. Years are the magnitude’s axis: the consumer names the year explicitly, and no rate exists for a year the council did not set.

The archive norm reads the foreign relation via a qualified package::symbol reference. The import is declared in the statute text with an exact version, and the law.toml manifest must match it.

Arxo Law
rule CopyFee strict {
label ru-KZ unofficial "Плата за копирование: число страниц по ставке города на год выставления счёта";
for o: CopyOrder;
for p: Person;
for n: Integer;
for y: Integer;
for rate: Money;
when copy_order(o, p, n) and billing_year(p, y) and tutorial.city::base_page_rate(y, rate);
then copy_fee(o, n * rate);
}

The label reads: “Copy fee: page count at the city rate for the billing year”.

Factscopy_fee(order1, 1050 KZT)
order for 7 pages, billed for 2026TRUE_ONLY
order for 7 pages, billed for 2025NEITHER

The test from this page, byte for byte; it executes on a merged world — the page’s CLIR together with the pinned bytes of tutorial.city:

Arxo Law
test "семь страниц по ставке города — 1050" {
given {
context {
decision_time @2026-04-20T09:00:00+05:00;
knowledge_time @2026-04-20T09:00:00+05:00;
legal_time @2026-04-20;
timezone "Asia/Almaty";
}
assert billing_year(entity_ref("urn:tutorial:ivanova"), 2026) {
id "assert-year";
origin case_input;
}
assert copy_order(entity_ref("urn:tutorial:order1"), entity_ref("urn:tutorial:ivanova"), 7) {
id "assert-order";
origin case_input;
}
}
evaluate truth(copy_fee(entity_ref("urn:tutorial:order1"), 1050 KZT));
expect truth_status == TRUE_ONLY;
expect evaluation_status == COMPUTED;
}

The test name reads: “Seven pages at the city rate — 1050.”

Note the test does not feed the rate as a fact: it arrives from the council package together with the world. The table’s second row is a year with no decision: the norm stays silent, and no “default rate” appears at the archive.

Three files hold the link between packages, each with its role.

Output
[package]
name = "tutorial.archive"
version = "0.15.0"
language = "0.2"
namespace = "urn:law:tutorial:archive"
[dependencies]
"tutorial.city" = "0.1.0"
"calc.tiers" = "0.1.0"

law.toml is the author’s intent: which packages and versions are needed. law.lock is this intent resolved into exact versions with the byte hash of each dependency: the council package’s contentHash is recorded in the lock, and if the council decision’s bytes change, the lock goes stale. deps/ holds the pinned bytes themselves, the dependency’s CLIR; only the generator writes them, they are not edited by hand.

The compiler needs these three files as a context: lowering a package with an imports context, where the resolver assembles the context from the lock and deps. Without a context the compiler refuses — a foreign head used to lower into an empty predicate silently, and the rule never fired. Today the lowered CLIR’s envelope.imports carries the dependencies’ hashes and enters the program’s semantic hash: the same archive against other council bytes is another program.

A case may not carry law: the test receives the program as a second argument, alongside the test file. A world is the consumer’s CLIR merged with the dependencies’ CLIR at the pinned hashes (link_deps): bytes diverging from the lock drop the world build with LDC-E1104 rather than substituting another rate. A chain longer than one step — an act importing a package that itself imports a third — assembles transitively, and each dependency’s pin is verified against the world participant that declared it (link_deps_transitive).

A merged world is not stored as a dependency: the lock pin counts over a package’s bytes, and a merged world’s bytes differ. Each package gets its own CLIR; a world is assembled per run.

The manifest’s second import, calc.tiers, is a shared tier computer; about it on the next page. The package’s full composition:

Output
packs/examples/tutorial/archive/
├── law.toml манифест: зависимости, features, [[tests]]
├── law.lock разрешение и хэши зависимостей — только генератором
├── deps/ закреплённые CLIR зависимостей — только генератором
├── package.law импорты, словарь, общие отношения
├── 10-tariffs.law плата за копирование и таблица скидок
├── 20-late-fee.law плата за просрочку шкалой calc.tiers
├── late-fee-cap.lawparam.json потолок платы по датам (A3)
├── sources.law провенанс: источник, редакция, фрагменты
└── tests/*.lawtest сценарии, объявленные в [[tests]]

The tree comments read: “manifest: dependencies, features, [[tests]]”; “resolution and dependency hashes — generator only”; “pinned dependency CLIRs — generator only”; “imports, vocabulary, shared relations”; “copy fee and discount table”; “overdue fee via the calc.tiers scale”; “fee cap by dates (A3)”; “provenance: source, edition, fragments”; “scenarios declared in [[tests]]”.

The practicum package is checked like calc.* consumers: statics, lock and deps freshness, lowering with a context and no empty predicates, a merged world, every scenario via both runners, and byte-identical results. This page is compiled under the same lock’s context and its test executes on the same world — the page can diverge from neither the language nor the package.

A symbol the neighbour does not export. Replace base_page_rate in CopyFee’s body with page_rate:

Output
error LDC-E1105: tutorial.city::page_rate: символ не экспортирован пакетом
(§24: межпакетные ссылки — только на `pub`-декларации; экспортировано:
base_page_rate, late_fee_unit)

The diagnostic reads: ‘symbol not exported by the package (cross-package references only to pub declarations; exported: base_page_rate, late_fee_unit)’.

The statute text’s version mismatched the resolved one. Ask for tutorial.city version "0.2.0":

Output
error LDC-E1110: зависимость "tutorial.city" объявлена версией "0.2.0", а
resolve-контекст предъявил "0.1.0" (§23: canonical build использует exact
version резолвера, и точная версия в тексте закона обязана с ней совпасть)

The diagnostic reads: ‘dependency “tutorial.city” is declared version “0.2.0” while the resolve context presented “0.1.0” (the statute text’s exact version must match the resolver’s exact version)’.

A package importing itself:

Output
error LDC-E1102: пакет "tutorial.archive" импортирует сам себя (§23:
imports acyclic)

The diagnostic reads: ‘package “tutorial.archive” imports itself (imports are acyclic)’.

All three refusals belong to the language under the practicum context. And one boundary to know: check without an imports context does not judge dependencies — a green check of a package with imports says nothing about the linkage. Judge by lowering with a context.

The rate arrived from someone else’s act; the tiered overdue-fee computation is likewise not worth writing yourself — it is shared computation, already in the tree: shared computation as a dependency.

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

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