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
Section titled “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, Run the world, Ask the case |
| Changing the corpus | publishing a version, changing the fee rate, migrating the shared vocabulary | Versions, hands-on, Stage A, Stage B |
| Structure and sources | structural queries, pinning, and extra checks; requires a source checkout (I) for query and gen | Read the structure, Provenance and shape |
Negative cases lists the expected no-answers of all three routes, and Templates holds the copy-paste aids the second route uses. Complete transcripts for every command are in the worked solutions.
Before you start
Section titled “Before you start”The install routes on the 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:
mkdir -p ~/corpus-lab-run && cd ~/corpus-lab-runcurl -O https://docs.arxo.io/build/corpus-lab-bundle.tar.gzcurl -O https://docs.arxo.io/build/corpus-lab-bundle.tar.gz.sha256shasum -a 256 -c corpus-lab-bundle.tar.gz.sha256tar xzf corpus-lab-bundle.tar.gzshasum -a 256 -c corpus-lab/MANIFEST.sha256cd corpus-labls -d fixtures/parcels-*mkdir -p /tmp/lab-workThe 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:
law versionExpected: 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.
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
Section titled “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 turns that into hands-on work. The other five fixture directories are later revisions of the same packages: Stage A and Stage B introduce them.
Run the world
Section titled “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.
law test fixtures/parcels-ifaceExpected last line: total: 2 checked, 2 passed, 0 failed, 0 not run; code 0.
The shared vocabulary holds on its own, with no dependencies.
law test fixtures/parcels-registryExpected 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.
law test fixtures/parcels-feeslaw test fixtures/parcels-appealsExpected 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.
law test fixtures/parcels-case1Expected 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, and why each silence holds explains each one.
Background reading on packages and worlds lives under several packages and a world and packages and cases.
Ask the case
Section titled “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.
law ask fixtures/parcels-case1 --case ParcelTransfer --query-json fixtures/parcels-case1/queries/fee-answered.json --format textExpected, in the answer section (## Ответ) of the recorded transcript:
Вычисление завершено.Установлено, что «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.
law ask fixtures/parcels-case1 --case ParcelTransfer --query-json fixtures/parcels-case1/queries/transfer-answered.json --format textExpected, in the answer section:
Вычисление завершено.Установлено, что «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.
law ask fixtures/parcels-case1 --case ParcelTransfer --query-json fixtures/parcels-case1/queries/appeal-answered.json --format textExpected, in the answer section:
Вычисление завершено.Установлено, что «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. 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.
Read the structure
Section titled “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:
law query --list 2>&1 | head -n 1law gen pinning 2>&1 | head -n 1law: unknown command querylaw: unknown command genLawQL questions read the formalization itself rather than any matter. The tour uses the catalogued questions; list the catalog first:
./law query --listTwenty-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:
./law query producers --project docs/corpus/lab/fixtures/parcels-registry --param predicate=urn:law:lab:parcels:registry#registered --param pkg=labparcels.registryOne row: the strict registration rule, with its anchor on the first article.
Who reads the holding link, across the whole lab world:
./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:
./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.registryOne 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:
./law query imports-of --project docs/corpus/lab/fixtures/parcels-case1 --param pkg=labparcels.case1Four rows, one per lab dependency. The full rows are recorded with the structure solutions. The reference for the question language is the LawQL page.
Provenance and shape
Section titled “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:
./law gen pinning docs/corpus/lab/fixtures/parcels-registryA 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:
./law audit docs/corpus/lab/fixtures/parcels-registryThird, 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:
law fix imports fixtures/parcels-registry --checkFourth, 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:
law engine manifest fixtures/parcels-registry/law.tomllaw engine lock fixtures/parcels-registry --checklaw install --offline --project fixtures/parcels-registryThe pinning drill is described in pin the act, edition, and publication, and the package mechanics in N. Package, context, snapshots, case, result, and test.
Versions, hands-on
Section titled “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:
law pack --out /tmp/lab-work/artifacts/iface --project fixtures/parcels-ifacelaw pack --out /tmp/lab-work/artifacts/registry --project fixtures/parcels-registrylaw pack --out /tmp/lab-work/artifacts/fees --project fixtures/parcels-feeslaw pack --out /tmp/lab-work/artifacts/appeals --project fixtures/parcels-appealslaw pack --out /tmp/lab-work/artifacts/case1 --project fixtures/parcels-case1law publish /tmp/lab-work/artifacts/iface --registry lab=/tmp/lab-work/registrylaw publish /tmp/lab-work/artifacts/registry --registry lab=/tmp/lab-work/registrylaw publish /tmp/lab-work/artifacts/fees --registry lab=/tmp/lab-work/registrylaw publish /tmp/lab-work/artifacts/appeals --registry lab=/tmp/lab-work/registrylaw publish /tmp/lab-work/artifacts/case1 --registry lab=/tmp/lab-work/registryCheck currency against that registry. Both pins report up to date:
law outdated --project fixtures/parcels-fees --registry lab=/tmp/lab-work/registryNow 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:
law update 'labparcels.registry@0.2.0' --project /tmp/lab-work/fees-upd --registry lab=/tmp/lab-work/registry --dry-runThen 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.
Stage A — a meaning change with the same signature
Section titled “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:
law test fixtures/parcels-fees-0.3.0Expected 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:
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:
mkdir -p /tmp/lab-work/stage-a/artifacts /tmp/lab-work/stage-a/answerslaw pack --out /tmp/lab-work/stage-a/artifacts/iface --project fixtures/parcels-ifacelaw pack --out /tmp/lab-work/stage-a/artifacts/registry --project fixtures/parcels-registrylaw pack --out /tmp/lab-work/stage-a/artifacts/fees --project fixtures/parcels-feeslaw pack --out /tmp/lab-work/stage-a/artifacts/appeals --project fixtures/parcels-appealslaw pack --out /tmp/lab-work/stage-a/artifacts/case1 --project fixtures/parcels-case1law pack --out /tmp/lab-work/stage-a/artifacts/fees-0.3.0 --project fixtures/parcels-fees-0.3.0law publish /tmp/lab-work/stage-a/artifacts/iface --registry lab=/tmp/lab-work/stage-a/registrylaw publish /tmp/lab-work/stage-a/artifacts/registry --registry lab=/tmp/lab-work/stage-a/registrylaw publish /tmp/lab-work/stage-a/artifacts/fees --registry lab=/tmp/lab-work/stage-a/registrylaw publish /tmp/lab-work/stage-a/artifacts/appeals --registry lab=/tmp/lab-work/stage-a/registrylaw publish /tmp/lab-work/stage-a/artifacts/case1 --registry lab=/tmp/lab-work/stage-a/registrylaw publish /tmp/lab-work/stage-a/artifacts/fees-0.3.0 --registry lab=/tmp/lab-work/stage-a/registrycp -r fixtures/parcels-case1 /tmp/lab-work/stage-a/case1Before 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.
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 1law 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 1law 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 1law 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:
law update 'labparcels.fees@0.3.0' --project /tmp/lab-work/stage-a/case1 --registry lab=/tmp/lab-work/stage-a/registry --dry-runlaw update 'labparcels.fees@0.3.0' --project /tmp/lab-work/stage-a/case1 --registry lab=/tmp/lab-work/stage-a/registrylaw test /tmp/lab-work/stage-a/case1law 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.lawtestlaw test /tmp/lab-work/stage-a/case1 | tail -n 1law test fixtures/parcels-fees-0.3.0 | tail -n 1law test fixtures/parcels-appeals | tail -n 1The grep filters keep the Russian answer lines Установлено
(established), Не установлено (not established), and Вычисление
(evaluation), plus the resultHash footer; the
answer glossary 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 aid.
Stage B — a shared-vocabulary interface change
Section titled “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:
law test fixtures/parcels-iface-0.2.0law test fixtures/parcels-registry-0.3.0law test fixtures/parcels-fees-0.4.0law test fixtures/parcels-appeals-0.2.0Expect 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. The renamed declaration:
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:
mkdir -p /tmp/lab-work/stage-b/artifactslaw pack --out /tmp/lab-work/stage-b/artifacts/iface --project fixtures/parcels-ifacelaw pack --out /tmp/lab-work/stage-b/artifacts/registry --project fixtures/parcels-registrylaw pack --out /tmp/lab-work/stage-b/artifacts/fees --project fixtures/parcels-feeslaw pack --out /tmp/lab-work/stage-b/artifacts/appeals --project fixtures/parcels-appealslaw pack --out /tmp/lab-work/stage-b/artifacts/case1 --project fixtures/parcels-case1law pack --out /tmp/lab-work/stage-b/artifacts/iface-0.2.0 --project fixtures/parcels-iface-0.2.0law pack --out /tmp/lab-work/stage-b/artifacts/registry-0.3.0 --project fixtures/parcels-registry-0.3.0law pack --out /tmp/lab-work/stage-b/artifacts/fees-0.4.0 --project fixtures/parcels-fees-0.4.0law pack --out /tmp/lab-work/stage-b/artifacts/appeals-0.2.0 --project fixtures/parcels-appeals-0.2.0law publish /tmp/lab-work/stage-b/artifacts/iface --registry lab=/tmp/lab-work/stage-b/registrylaw publish /tmp/lab-work/stage-b/artifacts/registry --registry lab=/tmp/lab-work/stage-b/registrylaw publish /tmp/lab-work/stage-b/artifacts/fees --registry lab=/tmp/lab-work/stage-b/registrylaw publish /tmp/lab-work/stage-b/artifacts/appeals --registry lab=/tmp/lab-work/stage-b/registrylaw publish /tmp/lab-work/stage-b/artifacts/case1 --registry lab=/tmp/lab-work/stage-b/registrylaw publish /tmp/lab-work/stage-b/artifacts/iface-0.2.0 --registry lab=/tmp/lab-work/stage-b/registrylaw publish /tmp/lab-work/stage-b/artifacts/registry-0.3.0 --registry lab=/tmp/lab-work/stage-b/registrylaw publish /tmp/lab-work/stage-b/artifacts/fees-0.4.0 --registry lab=/tmp/lab-work/stage-b/registrylaw publish /tmp/lab-work/stage-b/artifacts/appeals-0.2.0 --registry lab=/tmp/lab-work/stage-b/registrycp -r fixtures/parcels-registry /tmp/lab-work/stage-b/reg-oldcp -r fixtures/parcels-case1 /tmp/lab-work/stage-b/case1-holdscp -r fixtures/parcels-fees /tmp/lab-work/stage-b/fees-conflictFirst 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:
law update 'labparcels.iface@0.2.0' --project /tmp/lab-work/stage-b/reg-old --registry lab=/tmp/lab-work/stage-b/registry --dry-runlaw update 'labparcels.iface@0.2.0' --project /tmp/lab-work/stage-b/fees-conflict --registry lab=/tmp/lab-work/stage-b/registry --dry-runThen 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:
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.lawtestcp /tmp/lab-work/stage-b/case1-holds/package.law /tmp/lab-work/stage-b/case1-body.lawpython3 -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/.lawlaw add 'labparcels.iface@0.2.0' --project /tmp/lab-work/stage-b/case1-holds --registry lab=/tmp/lab-work/stage-b/registry --from lablaw add 'labparcels.registry@0.3.0' --project /tmp/lab-work/stage-b/case1-holds --registry lab=/tmp/lab-work/stage-b/registry --from lablaw add 'labparcels.fees@0.4.0' --project /tmp/lab-work/stage-b/case1-holds --registry lab=/tmp/lab-work/stage-b/registry --from lablaw add 'labparcels.appeals@0.2.0' --project /tmp/lab-work/stage-b/case1-holds --registry lab=/tmp/lab-work/stage-b/registry --from labperl -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.lawcp /tmp/lab-work/stage-b/case1-body.law /tmp/lab-work/stage-b/case1-holds/package.lawlaw test /tmp/lab-work/stage-b/case1-holdsEvery 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 aid alongside the public vocabulary contract promises the rename had to renegotiate.
Negative cases
Section titled “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
Section titled “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 — scope, sources, owners, and release criteria of a corpus area.
- Public vocabulary contract — the stability promises a shared vocabulary makes to consumers.
- Vocabulary decision record — one naming decision with context, options, and effect.
- Shared dependency change — interface and meaning changes with consumer checks.
- Corpus review checklist — the checks each change passes before release.
- Release checklist — 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 — the parcel area, its package set, and the verification runs on record.
- Filled public vocabulary contract — the stability promises of the shared parcel words.
- Filled vocabulary decision record — why the parcel kind is a closed three-member enum.
- Filled shared dependency change — moving the fees consumer across a registry revision.
- Filled corpus review checklist — the registry review with its evidence fences.
- Filled release checklist — the registry release with its publication line.
Reproduction details
Section titled “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:
law 0.1.1semantics: law.core/0.2std for language 0.2: 0.2.0lawql: lawql/1 (queryResult 0.1)binary hash: sha256:cfa7c17232f2dc594e665dbbf2ad3c154675797ef4be07ef7d758c9abfb80a28The 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:
d274971f1618db195ea795803e9fd968c4d366903acd807ff9f2a573d0e33d0b corpus-lab-bundle.tar.gzFull transcripts for every command of the tour, including the fixture listing and the version check, are in the worked solutions.
Further reading
Section titled “Further reading”- A package in the corpus
- N. Package, context, snapshots, case, result, and test
- LawQL
- Several packages and a world
- Packages and cases
- Pin the act, edition, and publication
- Versions
Worked solutions with complete transcripts: solutions.
Documentation for Arxo. Writings — blog.arxo.io.
Anonymous visit counts on stats.arxo.io, no cookies.