# Several packages and a world 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. ```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); } ``` ## Someone else's act 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. ```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. ## A reference across the boundary 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. ```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". | Facts | `copy_fee(order1, 1050 KZT)` | |---|---| | order for 7 pages, billed for 2026 | `TRUE_ONLY` | | order for 7 pages, billed for 2025 | `NEITHER` | 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`: ```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. ## Manifest, lock, and dependency bytes Three files hold the link between packages, each with its role. ```text [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. ## World and 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. ## What else lies in the practicum package The manifest's second import, `calc.tiers`, is a shared tier computer; about it on [the next page](/tutorials/primitives/). The package's full composition: ```text 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. ## Compiler refusals A symbol the neighbour does not export. Replace `base_page_rate` in `CopyFee`'s body with `page_rate`: ```text 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"`: ```text 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: ```text 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. ## Next 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](/tutorials/primitives/).