# The lab: parcels fixtures tour Five small fictional packages form a parcel registry that never existed: parcels, owners, registration, fees, appeals, and one worked case. The domain is invented on purpose, so no training sentence can be mistaken for law. Every command below runs against these fixtures, and every expected result on this page was recorded by running the tool. Environment: the public `law-v0.1.1` release on `PATH`, commands run from the extracted bundle root (`corpus-lab/`), input files from the lab archive. One route also needs a source checkout; its section says so. ## Choose a route The lab supports three routes. Each one stands on its own; start with the first run if this is your first visit. | Route | What the reader gets | Sections | |---|---|---| | First run | five initial packages, their tests, three questions on the case, and how to read the answers | [Before you start](#before-you-start), [Run the world](#run-the-world), [Ask the case](#ask-the-case) | | Changing the corpus | publishing a version, changing the fee rate, migrating the shared vocabulary | [Versions, hands-on](#versions-hands-on), [Stage A](#stage-a--a-meaning-change-with-the-same-signature), [Stage B](#stage-b--a-shared-vocabulary-interface-change) | | Structure and sources | structural queries, pinning, and extra checks; **requires a source checkout** (I) for `query` and `gen` | [Read the structure](#read-the-structure), [Provenance and shape](#provenance-and-shape) | [Negative cases](#negative-cases) lists the expected no-answers of all three routes, and [Templates](#templates) holds the copy-paste aids the second route uses. Complete transcripts for every command are in the [worked solutions](/corpus/lab/solutions/). ## Before you start The install routes on the [Install](/cli/install/) page deliver the binary only: a package manager, a setup script, or a release archive, each carrying the same release bytes. None of them carries the tour fixtures. The fixtures, templates, and check scripts ship as one self-contained archive instead — start from an empty directory: ```shell mkdir -p ~/corpus-lab-run && cd ~/corpus-lab-run curl -O https://docs.arxo.io/build/corpus-lab-bundle.tar.gz curl -O https://docs.arxo.io/build/corpus-lab-bundle.tar.gz.sha256 shasum -a 256 -c corpus-lab-bundle.tar.gz.sha256 tar xzf corpus-lab-bundle.tar.gz shasum -a 256 -c corpus-lab/MANIFEST.sha256 cd corpus-lab ls -d fixtures/parcels-* mkdir -p /tmp/lab-work ``` The first check confirms the download. The manifest check confirms every extracted file. Run it from the directory that holds `corpus-lab/`, because the manifest paths carry that prefix. Run it without a pipe, so that a failure exit is not lost. The listing names ten fixture directories: the five without a version suffix are the first run; the suffixed ones are later revisions, introduced in the exercise that uses them. Everything the tour writes lands under `/tmp/lab-work`; the fixtures stay unchanged. Every fixture path in the tour is relative to the bundle root (`fixtures/...`). Then confirm the build: ```shell law version ``` Expected: the first line reads `law 0.1.1`. This means the public release the transcripts were recorded on is the one on your `PATH`. The full block, with the per-platform binary hash, is under [Reproduction details](#reproduction-details). Do not substitute the checkout `./law` launcher for the tour. It builds newer source whose 0.2.6 numerals disagree with these 0.2.4 fixtures, and the fee scenario fails there. The checkout is needed only by the two sections of the third route, and each of them names its own prerequisites. ## The fixtures The first run uses five packages: | fixture | package | version | role | |---|---|---|---| | parcels-iface | labparcels.iface | 0.1.0 | shared vocabulary: parcels, owners, the holding link, the kind list | | parcels-registry | labparcels.registry | 0.1.0 | the act: two pinned articles plus the rules that apply them | | parcels-fees | labparcels.fees | 0.2.0 | first consumer: the fee each registered parcel owes | | parcels-appeals | labparcels.appeals | 0.1.0 | second consumer: whether an appeal arrived inside its window | | parcels-case1 | labparcels.case1 | 0.1.0 | the worked case: one parcel, three questions, recorded answers | All five declare the same language line and the explicit import style. The fees package sits at 0.2.0 while its neighbours sit at 0.1.0, so the world mixes versions from the start; the [versions exercise](#versions-hands-on) turns that into hands-on work. The other five fixture directories are later revisions of the same packages: [Stage A](#stage-a--a-meaning-change-with-the-same-signature) and [Stage B](#stage-b--a-shared-vocabulary-interface-change) introduce them. ## Run the world Each fixture ships its scenarios and its pinned world, so `law test` passes with no setup. Run the five in dependency order. ```shell law test fixtures/parcels-iface ``` Expected last line: `total: 2 checked, 2 passed, 0 failed, 0 not run; code 0`. The shared vocabulary holds on its own, with no dependencies. ```shell law test fixtures/parcels-registry ``` Expected last line: `total: 4 checked, 4 passed, 0 failed, 0 not run; code 0`. The act works over the shared vocabulary: registration and transfer, each with one case where nothing should follow. ```shell law test fixtures/parcels-fees law test fixtures/parcels-appeals ``` Expected last lines: `total: 5 checked, 5 passed, 0 failed, 0 not run; code 0` for the fees and `total: 3 checked, 3 passed, 0 failed, 0 not run; code 0` for the appeals. Both consumers read the act through their pins. ```shell law test fixtures/parcels-case1 ``` Expected last line: `total: 4 checked, 4 passed, 0 failed, 0 not run; code 0`. The whole five-package world is consistent, and the case can be asked. Several scenarios expect silence rather than a verdict: a stranger's holding, a missing kind, a late filing, a wrong amount. Silence there is the correct behaviour. The full transcripts, with every scenario name, are the [first worked solution](/corpus/lab/solutions/#scenario-transcripts), and [why each silence holds](/corpus/lab/solutions/#why-each-silence-holds) explains each one. Background reading on packages and worlds lives under [several packages and a world](/tutorials/packages/) and [packages and cases](/cli/packages-cases/). ## Ask the case The parcels-case1 fixture pins one matter: Amy holds parcel p1, the parcel is residential, Amy asks to transfer it to Bob, and an appeal arrives on day 25 of a window running from day 10 to day 40. Three question files ship with the fixture; each asks whether one conclusion holds. ```shell law ask fixtures/parcels-case1 --case ParcelTransfer --query-json fixtures/parcels-case1/queries/fee-answered.json --format text ``` Expected, in the answer section (`## Ответ`) of the recorded transcript: ```text Вычисление завершено. Установлено, что «fee_due» (parcel: `p1`, сумма: 100); обратное не установлено. ``` The answer shell speaks Russian while the fixture words stay English. The first line says the evaluation completed. The second line says the fee of 100 for p1 is established and its opposite is not. The grounds below it lead from the rule `FeeResidential` to the registration rule, which cites article 1 of the pinned edition, and then to the two case facts it used. ```shell law ask fixtures/parcels-case1 --case ParcelTransfer --query-json fixtures/parcels-case1/queries/transfer-answered.json --format text ``` Expected, in the answer section: ```text Вычисление завершено. Установлено, что «transfer_granted» (parcel: `p1`, from: `amy`, to: `bob`); обратное не установлено. ``` The transfer from Amy to Bob is established. Its first ground cites article 2; the registration step under it cites article 1. ```shell law ask fixtures/parcels-case1 --case ParcelTransfer --query-json fixtures/parcels-case1/queries/appeal-answered.json --format text ``` Expected, in the answer section: ```text Вычисление завершено. Установлено, что «appeal_timely» (parcel: `p1`); обратное не установлено. ``` The appeal is timely: the grounds name the filing on day 25 and the window from day 10 to day 40 as case facts. All three conclusions are established, and each proof cites the case facts and pinned articles it used. The complete records, with the footer hashes and a reading guide, are in the [answers solution](/corpus/lab/solutions/#answers). One question file needs a warning: the amount travels in the compiled Integer shape, and the solutions show why that spelling is required. How a case package is put together is covered in [a package in the corpus](/tutorials/package/). ## Read the structure **Requires a source checkout** (I). Prerequisites for this section only: a full repository checkout, commands run from the repository root with the `./law` launcher. The public composition is smaller than the checkout one: it has no `query` and no `gen` command. To confirm that boundary on the public build: ```shell law query --list 2>&1 | head -n 1 law gen pinning 2>&1 | head -n 1 ``` ```text law: unknown command query law: unknown command gen ``` LawQL questions read the formalization itself rather than any matter. The tour uses the catalogued questions; list the catalog first: ```shell ./law query --list ``` Twenty-two entries answer: six named questions and sixteen checks. Five questions below run against the fixtures; each names its target package explicitly, so the snapshot stays inside the lab world. Who derives the registered flag: ```shell ./law query producers --project docs/corpus/lab/fixtures/parcels-registry --param predicate=urn:law:lab:parcels:registry#registered --param pkg=labparcels.registry ``` One row: the strict registration rule, with its anchor on the first article. Who reads the holding link, across the whole lab world: ```shell ./law query readers --project docs/corpus/lab/fixtures/parcels-case1 --param predicate=urn:law:lab:parcels:iface#owns --param pkg='labparcels.*' ``` Two rows: both registry rules, each with the article behind it. What the registration rule leans on, and how the parcel type resolves: ```shell ./law query anchors-of --project docs/corpus/lab/fixtures/parcels-registry --param node=urn:law:lab:parcels:registry#RegistrationComplete --param pkg=labparcels.registry ./law query type-step --project docs/corpus/lab/fixtures/parcels-registry --param type=urn:law:lab:parcels:registry#Parcel --param pkg=labparcels.registry ``` One anchor row pointing at the first article; one alias step from the registry parcel type to the shared parcel type. What the case package pulls in: ```shell ./law query imports-of --project docs/corpus/lab/fixtures/parcels-case1 --param pkg=labparcels.case1 ``` Four rows, one per lab dependency. The full rows are recorded with the structure solutions. The reference for the question language is the [LawQL page](/lawql/). ## Provenance and shape The registry fixture pins two invented articles and derives both of them through rules. Four readings confirm the chain from text to machine. The first two **require a source checkout** (I): full repository, `./law` from the repository root. The public composition has no `gen`, and its audit cannot see the lint catalog. First, the pinning check, which compares the fragment block in the sources file against the pinned bytes: ```shell ./law gen pinning docs/corpus/lab/fixtures/parcels-registry ``` A tick and two articles answer. Second, the audit over the same fixture, which reports both articles executable, the text share they cover, and sixteen structural checks with no findings: ```shell ./law audit docs/corpus/lab/fixtures/parcels-registry ``` Third, the import-block check on the registry package. The package keeps its source model and its rules in two files, and the rules file names the fragments it leans on in a block the tool maintains. Confirm the block is canonical with the per-package form of the command, which is the only form to use inside a shared tree: ```shell law fix imports fixtures/parcels-registry --check ``` Fourth, the manifest and lock readings on any fixture, plus the offline install, which confirms the pinned files already match the lock without touching the network: ```shell law engine manifest fixtures/parcels-registry/law.toml law engine lock fixtures/parcels-registry --check law install --offline --project fixtures/parcels-registry ``` The pinning drill is described in [pin the act, edition, and publication](/recipes/i-sources/pin-publication/), and the package mechanics in [N. Package, context, snapshots, case, result, and test](/recipes/n-package/). ## Versions, hands-on Everything so far was read-only. This part writes, so it works on scratch copies outside the bundle and leaves the fixtures pristine. The goal: publish the lab packages to a throwaway registry, watch the currency check pass, author a new registry revision, watch the check flag it, and move one consumer across. Build the registry from the fixtures. Pack each package, then publish each artifact under one registry name: ```shell law pack --out /tmp/lab-work/artifacts/iface --project fixtures/parcels-iface law pack --out /tmp/lab-work/artifacts/registry --project fixtures/parcels-registry law pack --out /tmp/lab-work/artifacts/fees --project fixtures/parcels-fees law pack --out /tmp/lab-work/artifacts/appeals --project fixtures/parcels-appeals law pack --out /tmp/lab-work/artifacts/case1 --project fixtures/parcels-case1 law publish /tmp/lab-work/artifacts/iface --registry lab=/tmp/lab-work/registry law publish /tmp/lab-work/artifacts/registry --registry lab=/tmp/lab-work/registry law publish /tmp/lab-work/artifacts/fees --registry lab=/tmp/lab-work/registry law publish /tmp/lab-work/artifacts/appeals --registry lab=/tmp/lab-work/registry law publish /tmp/lab-work/artifacts/case1 --registry lab=/tmp/lab-work/registry ``` Check currency against that registry. Both pins report up to date: ```shell law outdated --project fixtures/parcels-fees --registry lab=/tmp/lab-work/registry ``` Now author revision 0.2.0 of the registry package on a scratch copy: copy the parcels-registry fixture aside, raise its version in the manifest, in both law files, and in the four scenario files, add one small derived rule, and confirm its four scenarios still pass on the copied pins with no rewiring step. Pack and publish the revision into the same registry. Currency now flags the new revision and exits nonzero; the solutions record the exact lines. Move a scratch copy of the fees consumer across. First preview: ```shell law update 'labparcels.registry@0.2.0' --project /tmp/lab-work/fees-upd --registry lab=/tmp/lab-work/registry --dry-run ``` Then run the same command without the preview flag, and run the consumer scenarios plus the currency check again: five passing scenarios, both pins up to date. Asking for a revision that was never published is refused; the solutions show that refusal too. Version rules and release practice are covered in [versions](/protocols/versions/). ## Stage A — a meaning change with the same signature This stage uses one more fixture, a later revision of the fee consumer: | fixture | package | version | role | |---|---|---|---| | parcels-fees-0.3.0 | labparcels.fees | 0.3.0 | the residential rate moves, the signature stays | The fee revision at 0.3.0 moves the residential rate from 100 to 120. Nothing else changes: the same predicate, the same argument shapes, the same commercial and garden rates. Confirm the revision passes on its own over the original world: ```shell law test fixtures/parcels-fees-0.3.0 ``` Expected last line: `total: 5 checked, 5 passed, 0 failed, 0 not run; code 0`. The revision is consistent by itself; the stage below shows what it changes for the case. The rule that moves: ```law fixture="parcels-fees-0.3.0/package.law" rule FeeResidential strict { for p: Parcel; for k: ParcelKind; when labparcels.registry::registered(p) and labparcels.registry::kind_of(p, k) and k == labparcels.iface::residential; then fee_due(p, 120); } ``` Work on scratch copies under a stage directory, with its own throwaway registry holding the original five states plus the fee revision: ```shell mkdir -p /tmp/lab-work/stage-a/artifacts /tmp/lab-work/stage-a/answers law pack --out /tmp/lab-work/stage-a/artifacts/iface --project fixtures/parcels-iface law pack --out /tmp/lab-work/stage-a/artifacts/registry --project fixtures/parcels-registry law pack --out /tmp/lab-work/stage-a/artifacts/fees --project fixtures/parcels-fees law pack --out /tmp/lab-work/stage-a/artifacts/appeals --project fixtures/parcels-appeals law pack --out /tmp/lab-work/stage-a/artifacts/case1 --project fixtures/parcels-case1 law pack --out /tmp/lab-work/stage-a/artifacts/fees-0.3.0 --project fixtures/parcels-fees-0.3.0 law publish /tmp/lab-work/stage-a/artifacts/iface --registry lab=/tmp/lab-work/stage-a/registry law publish /tmp/lab-work/stage-a/artifacts/registry --registry lab=/tmp/lab-work/stage-a/registry law publish /tmp/lab-work/stage-a/artifacts/fees --registry lab=/tmp/lab-work/stage-a/registry law publish /tmp/lab-work/stage-a/artifacts/appeals --registry lab=/tmp/lab-work/stage-a/registry law publish /tmp/lab-work/stage-a/artifacts/case1 --registry lab=/tmp/lab-work/stage-a/registry law publish /tmp/lab-work/stage-a/artifacts/fees-0.3.0 --registry lab=/tmp/lab-work/stage-a/registry cp -r fixtures/parcels-case1 /tmp/lab-work/stage-a/case1 ``` Before moving anything, save the three old answers and replay the fee answer against its saved inputs. The replay reaches the same hash, which is the point: old inputs, old answer, no drift. ```shell law ask /tmp/lab-work/stage-a/case1 --case ParcelTransfer --query-json /tmp/lab-work/stage-a/case1/queries/fee-answered.json --format text --out /tmp/lab-work/stage-a/answers/fee-answered | tail -n 1 law ask /tmp/lab-work/stage-a/case1 --case ParcelTransfer --query-json /tmp/lab-work/stage-a/case1/queries/transfer-answered.json --format text --out /tmp/lab-work/stage-a/answers/transfer-answered | tail -n 1 law ask /tmp/lab-work/stage-a/case1 --case ParcelTransfer --query-json /tmp/lab-work/stage-a/case1/queries/appeal-answered.json --format text --out /tmp/lab-work/stage-a/answers/appeal-answered | tail -n 1 law eval /tmp/lab-work/stage-a/answers/fee-answered | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['resultHash'], d['results'][0]['truthStatus'], d['results'][0]['evaluationStatus'])" ``` Now predict the move before running it. After the case crosses to the fee revision, the old residential amount goes silent, the new amount is established, and the other two verdicts stand while their hashes move with the changed program bytes: | question | before | after | |---|---|---| | residential fee of 100 | established | silent | | residential fee of 120 | silent | established | | transfer from Amy to Bob | established | established, new hash | | timely appeal | established | established, new hash | Move the scratch case across, confirm the four predictions, and run both consumers plus the case. The case run breaks exactly where predicted — its fee expectation still names the old amount — so move that expectation to the new amount and rerun until every test passes: ```shell law update 'labparcels.fees@0.3.0' --project /tmp/lab-work/stage-a/case1 --registry lab=/tmp/lab-work/stage-a/registry --dry-run law update 'labparcels.fees@0.3.0' --project /tmp/lab-work/stage-a/case1 --registry lab=/tmp/lab-work/stage-a/registry law test /tmp/lab-work/stage-a/case1 law ask /tmp/lab-work/stage-a/case1 --case ParcelTransfer --query-json /tmp/lab-work/stage-a/case1/queries/fee-answered.json --format text | grep -E '^(Установлено|Не установлено|Вычисление)|resultHash' law ask /tmp/lab-work/stage-a/case1 --case ParcelTransfer --query 'evaluate truth(labparcels.fees::fee_due(entity_ref("urn:lab:parcel:p1"), 120));' --query-id fee-120 --format text | grep -E '^(Установлено|Не установлено|Вычисление)|resultHash' law ask /tmp/lab-work/stage-a/case1 --case ParcelTransfer --query-json /tmp/lab-work/stage-a/case1/queries/transfer-answered.json --format text | grep -E '^(Установлено|Не установлено)|resultHash' law ask /tmp/lab-work/stage-a/case1 --case ParcelTransfer --query-json /tmp/lab-work/stage-a/case1/queries/appeal-answered.json --format text | grep -E '^(Установлено|Не установлено)|resultHash' perl -pi -e 's/fee_due\(entity_ref\("urn:lab:parcel:p1"\), 100\)/fee_due(entity_ref("urn:lab:parcel:p1"), 120)/' /tmp/lab-work/stage-a/case1/tests/01-worked-parcel.lawtest law test /tmp/lab-work/stage-a/case1 | tail -n 1 law test fixtures/parcels-fees-0.3.0 | tail -n 1 law test fixtures/parcels-appeals | tail -n 1 ``` The `grep` filters keep the Russian answer lines `Установлено` (established), `Не установлено` (not established), and `Вычисление` (evaluation), plus the `resultHash` footer; the [answer glossary](/corpus/lab/solutions/#answers) translates every recurring phrase. The solutions record every line: the saved footers, the replay, the preview and the move, the predicted break, the four verdicts after the move, and the passing runs. The change template for a meaning shift is the [shared dependency change](/build/corpus-lab-templates/shared-dependency-change.md) aid. ## Stage B — a shared-vocabulary interface change This stage uses four more fixtures, the renamed line: | fixture | package | version | role | |---|---|---|---| | parcels-iface-0.2.0 | labparcels.iface | 0.2.0 | the shared vocabulary with the holding link renamed | | parcels-registry-0.3.0 | labparcels.registry | 0.3.0 | the act migrated to the renamed link | | parcels-fees-0.4.0 | labparcels.fees | 0.4.0 | the fee consumer on the renamed line, original rates | | parcels-appeals-0.2.0 | labparcels.appeals | 0.2.0 | the appeal consumer on the renamed line | The renamed line renames the holding link of the shared vocabulary from `owns` to `holds`, keeping its shape: same owner and parcel arguments, same key. The registry, fee, and appeal revisions follow the rename; rates and windows stay at their original values. Stage A and Stage B are alternative futures that branch from the original states, not a single timeline, so each can be run on its own. Confirm the four revisions pass: ```shell law test fixtures/parcels-iface-0.2.0 law test fixtures/parcels-registry-0.3.0 law test fixtures/parcels-fees-0.4.0 law test fixtures/parcels-appeals-0.2.0 ``` Expect 2, 4, 5, and 3 passing scenarios, the same totals as the original states: the rename keeps every scenario. The full transcripts are with the [scenario solutions](/corpus/lab/solutions/#scenario-transcripts). The renamed declaration: ```law fixture="parcels-iface-0.2.0/package.law" pub relation holds(owner: Owner, parcel: Parcel) kind institutional { key(parcel); label en unofficial "owner holds the parcel"; } ``` Build a second stage directory whose registry holds the original five states plus the four renamed revisions: ```shell mkdir -p /tmp/lab-work/stage-b/artifacts law pack --out /tmp/lab-work/stage-b/artifacts/iface --project fixtures/parcels-iface law pack --out /tmp/lab-work/stage-b/artifacts/registry --project fixtures/parcels-registry law pack --out /tmp/lab-work/stage-b/artifacts/fees --project fixtures/parcels-fees law pack --out /tmp/lab-work/stage-b/artifacts/appeals --project fixtures/parcels-appeals law pack --out /tmp/lab-work/stage-b/artifacts/case1 --project fixtures/parcels-case1 law pack --out /tmp/lab-work/stage-b/artifacts/iface-0.2.0 --project fixtures/parcels-iface-0.2.0 law pack --out /tmp/lab-work/stage-b/artifacts/registry-0.3.0 --project fixtures/parcels-registry-0.3.0 law pack --out /tmp/lab-work/stage-b/artifacts/fees-0.4.0 --project fixtures/parcels-fees-0.4.0 law pack --out /tmp/lab-work/stage-b/artifacts/appeals-0.2.0 --project fixtures/parcels-appeals-0.2.0 law publish /tmp/lab-work/stage-b/artifacts/iface --registry lab=/tmp/lab-work/stage-b/registry law publish /tmp/lab-work/stage-b/artifacts/registry --registry lab=/tmp/lab-work/stage-b/registry law publish /tmp/lab-work/stage-b/artifacts/fees --registry lab=/tmp/lab-work/stage-b/registry law publish /tmp/lab-work/stage-b/artifacts/appeals --registry lab=/tmp/lab-work/stage-b/registry law publish /tmp/lab-work/stage-b/artifacts/case1 --registry lab=/tmp/lab-work/stage-b/registry law publish /tmp/lab-work/stage-b/artifacts/iface-0.2.0 --registry lab=/tmp/lab-work/stage-b/registry law publish /tmp/lab-work/stage-b/artifacts/registry-0.3.0 --registry lab=/tmp/lab-work/stage-b/registry law publish /tmp/lab-work/stage-b/artifacts/fees-0.4.0 --registry lab=/tmp/lab-work/stage-b/registry law publish /tmp/lab-work/stage-b/artifacts/appeals-0.2.0 --registry lab=/tmp/lab-work/stage-b/registry cp -r fixtures/parcels-registry /tmp/lab-work/stage-b/reg-old cp -r fixtures/parcels-case1 /tmp/lab-work/stage-b/case1-holds cp -r fixtures/parcels-fees /tmp/lab-work/stage-b/fees-conflict ``` First the break. Moving the unmigrated act under the renamed vocabulary is refused before anything is written: the refusal names the missing word at both rules that read it and lists the new word among the exports. A second refusal follows on the fee consumer — moving one of two coupled pins alone is a version conflict — which is why the migration below rewires pins together instead of moving them one by one: ```shell law update 'labparcels.iface@0.2.0' --project /tmp/lab-work/stage-b/reg-old --registry lab=/tmp/lab-work/stage-b/registry --dry-run law update 'labparcels.iface@0.2.0' --project /tmp/lab-work/stage-b/fees-conflict --registry lab=/tmp/lab-work/stage-b/registry --dry-run ``` Then the migration. Rename the holding link in the scratch case file and its scenarios, strip the four pins to a bare header, re-pin against the renamed revisions, and restore the body with the new import lines: ```shell perl -pi -e 's/labparcels\.iface::owns\(/labparcels.iface::holds(/g' /tmp/lab-work/stage-b/case1-holds/cases/parcel-transfer.lawcase /tmp/lab-work/stage-b/case1-holds/tests/01-worked-parcel.lawtest cp /tmp/lab-work/stage-b/case1-holds/package.law /tmp/lab-work/stage-b/case1-body.law python3 -c "t=open('/tmp/lab-work/stage-b/case1-holds/law.toml').read(); t=t.split('[dependencies]')[0].rstrip()+'\n'; open('/tmp/lab-work/stage-b/case1-holds/law.toml','w').write(t); h=open('/tmp/lab-work/stage-b/case1-holds/package.law').read().split('\n'); open('/tmp/lab-work/stage-b/case1-holds/package.law','w').write('\n'.join(h[:3])+'\n')" rm -rf /tmp/lab-work/stage-b/case1-holds/deps /tmp/lab-work/stage-b/case1-holds/law.lock /tmp/lab-work/stage-b/case1-holds/.law law add 'labparcels.iface@0.2.0' --project /tmp/lab-work/stage-b/case1-holds --registry lab=/tmp/lab-work/stage-b/registry --from lab law add 'labparcels.registry@0.3.0' --project /tmp/lab-work/stage-b/case1-holds --registry lab=/tmp/lab-work/stage-b/registry --from lab law add 'labparcels.fees@0.4.0' --project /tmp/lab-work/stage-b/case1-holds --registry lab=/tmp/lab-work/stage-b/registry --from lab law add 'labparcels.appeals@0.2.0' --project /tmp/lab-work/stage-b/case1-holds --registry lab=/tmp/lab-work/stage-b/registry --from lab perl -pi -e 's/import labparcels\.iface version "0.1.0"/import labparcels.iface version "0.2.0"/; s/import labparcels\.registry version "0.1.0"/import labparcels.registry version "0.3.0"/; s/import labparcels\.fees version "0.2.0"/import labparcels.fees version "0.4.0"/; s/import labparcels\.appeals version "0.1.0"/import labparcels.appeals version "0.2.0"/' /tmp/lab-work/stage-b/case1-body.law cp /tmp/lab-work/stage-b/case1-body.law /tmp/lab-work/stage-b/case1-holds/package.law law test /tmp/lab-work/stage-b/case1-holds ``` Every test of the recomposed world passes, and all three case questions are established again — including the residential fee of 100, which is the point of the stage: the words changed, the meaning held. The failing moves, the four dependency wires, and the three answers are recorded with the solutions. The interface side of the change template is the [shared dependency change](/build/corpus-lab-templates/shared-dependency-change.md) aid alongside the [public vocabulary contract](/build/corpus-lab-templates/public-vocabulary-contract.md) promises the rename had to renegotiate. ## Negative cases The lab ships its no-answers alongside its answers. Each item below is exercised by the fixtures and explained in the solutions: | case | expected behaviour | |---|---| | a stranger's holding | the vocabulary stays silent | | a holding without a recorded kind | registration stays silent | | a transfer asked by a non-owner | the grant stays silent | | a kind without a holding | no fee is due | | the wrong amount for a kind | the fee is not established | | a filing past the window end | timeliness is not established | | a filing without registration | timeliness is not established | | a currency check with a newer revision published | the pin is flagged, nonzero exit | | an update to a revision that was never published | the command is refused | | a fragment line edited by hand | the pinning check fails, and the diff names the line | | the old amount after the rate change | the old fee stays unestablished; updating the expectation fixes the test, not the model | | a consumer moved under renamed words without migration | the update is refused and names the missing word | | moving one of two coupled pins | refused as a version conflict; re-pin together | ## Templates The lab ships six copy-paste templates that the tour uses at each decision point. They are documentation aids, not engine input files: - [Corpus charter](/build/corpus-lab-templates/corpus-charter.md) — scope, sources, owners, and release criteria of a corpus area. - [Public vocabulary contract](/build/corpus-lab-templates/public-vocabulary-contract.md) — the stability promises a shared vocabulary makes to consumers. - [Vocabulary decision record](/build/corpus-lab-templates/vocabulary-decision-record.md) — one naming decision with context, options, and effect. - [Shared dependency change](/build/corpus-lab-templates/shared-dependency-change.md) — interface and meaning changes with consumer checks. - [Corpus review checklist](/build/corpus-lab-templates/review-checklist.md) — the checks each change passes before release. - [Release checklist](/build/corpus-lab-templates/release-checklist.md) — composition, passports, reports, and replay for a release. Each template also ships one filled example on lab material, with pasted engine outputs recorded by running the tool: - [Filled corpus charter](/build/corpus-lab-templates/filled/corpus-charter.md) — the parcel area, its package set, and the verification runs on record. - [Filled public vocabulary contract](/build/corpus-lab-templates/filled/public-vocabulary-contract.md) — the stability promises of the shared parcel words. - [Filled vocabulary decision record](/build/corpus-lab-templates/filled/vocabulary-decision-record.md) — why the parcel kind is a closed three-member enum. - [Filled shared dependency change](/build/corpus-lab-templates/filled/shared-dependency-change.md) — moving the fees consumer across a registry revision. - [Filled corpus review checklist](/build/corpus-lab-templates/filled/review-checklist.md) — the registry review with its evidence fences. - [Filled release checklist](/build/corpus-lab-templates/filled/release-checklist.md) — the registry release with its publication line. ## Reproduction details
Toolchain, hashes, and full transcripts Transcripts on this page and in the solutions quote standard output. Compare verdicts and proof shape when you repeat the tour. The long hashes in answer footers match whenever the inputs match. Pinned tool: the public `law-v0.1.1` release, observed through `law version` on macOS arm64: ```text law 0.1.1 semantics: law.core/0.2 std for language 0.2: 0.2.0 lawql: lawql/1 (queryResult 0.1) binary hash: sha256:cfa7c17232f2dc594e665dbbf2ad3c154675797ef4be07ef7d758c9abfb80a28 ``` The first line is the tool version; the last line hashes the bytes behind it, so record the whole block with your run. Linux x64 reports `sha256:be8b4fbb…` instead; both hashes are pinned in the bundle's `PINNED.md`, which also records the source revision of the archive. The checksum file pins the archive: ```text d274971f1618db195ea795803e9fd968c4d366903acd807ff9f2a573d0e33d0b corpus-lab-bundle.tar.gz ``` Full transcripts for every command of the tour, including the fixture listing and the version check, are in the [worked solutions](/corpus/lab/solutions/#before-you-start).
## Further reading - [A package in the corpus](/tutorials/package/) - [N. Package, context, snapshots, case, result, and test](/recipes/n-package/) - [LawQL](/lawql/) - [Several packages and a world](/tutorials/packages/) - [Packages and cases](/cli/packages-cases/) - [Pin the act, edition, and publication](/recipes/i-sources/pin-publication/) - [Versions](/protocols/versions/) Worked solutions with complete transcripts: [solutions](/corpus/lab/solutions/).