# Dependencies, locks, and registries Dependencies in the corpus resolve to exact pins, never to ranges. Three commands manage the closure, a fourth previews drift, and the lockfile remembers the outcome. All of them are **available in the public release** (S). Environment: the public `law-v0.1.1` release, run from the root of the [lab bundle](/corpus/lab/#before-you-start); the example needs the throwaway registry built in the lab's [versions exercise](/corpus/lab/solutions/#versions-exercise). Status letters follow the [legend on the topic index](/corpus/#how-this-topic-marks-confidence). ## Example: pin one dependency The lab's `parcels-registry` package imports the shared vocabulary `labparcels.iface`. The [wiring replay](/corpus/lab/solutions/#how-the-fixtures-were-wired) works on a scratch copy of that package with its `[dependencies]` section and its `law.lock` removed, and then pins the vocabulary again from the lab registry: ```shell law add 'labparcels.iface@0.1.0' --project /tmp/lab-work/wire/reg --registry lab=/tmp/lab-work/registry --from lab ``` The `law add` part of the recorded output (excerpt: the first six lines; the full block is under [Reproduction details](#reproduction-details)): ```text lab ← /tmp/lab-work/registry (--registry) exports (7): Owner, Parcel, ParcelKind, commercial, garden, owns, residential changed: .law/transport.json changed: deps/labparcels.iface.lawir.json changed: law.lock changed: law.toml ``` Line by line: the first line names the registry address the command read. The second lists what the pinned package exports to the importer. The `changed:` lines name every file the command wrote: - `law.toml` gains the requested pin under `[dependencies]`: `"labparcels.iface" = "0.1.0"`. This is the request: one exact version, no range. - `law.lock` is written from scratch. It records the resolved package (name, version, namespace, registry id `lab`, the resolver address `registry://lab/labparcels.iface/0.1.0`, and the content hash of its compiled bytes), the dependency edge from `labparcels.registry` to `labparcels.iface` with the requested and the resolved version, the semantics line `law.core/0.2.0`, the root package's own content hash, and one resolution hash over all of it. - `deps/labparcels.iface.lawir.json` holds the compiled bytes that the lock's content hash covers. - `.law/transport.json` is local service state of the command. It ships with no fixture. The shipped fixture shows the same end state; read [`fixtures/parcels-registry/law.lock`](/corpus/lab/fixtures/parcels-registry) next to the list above. What this result means: the package now has one exact, hash-checked version of its dependency, and every later check resolves through the lock. In the lab run the package's four scenarios pass right after the pin, so the pin is ready to commit. A package that names two dependencies cannot be wired in one call; the [wiring replay](/corpus/lab/solutions/#how-the-fixtures-were-wired) explains the one-at-a-time order. ## Adding, installing, updating, previewing - `add` records a new dependency and resolves the closure over registry descriptors. It also accepts a container file in place of a package name; then it reads no registry at all. - `install` materializes the pinned closure into the project. It alone offers an offline mode and a recovery mode, and it restores from a container through its transfer flag. - `update` moves a pin forward. It can replay saved evaluations under the candidate version. - `outdated` previews newer stable versions of each pinned package and checks whether the pinned bytes still match. It leaves the lock untouched. Only `install` runs offline. Adding and updating by name must read registry descriptors to build the closure. Registries are local directories, so offline here means no registry reads at all (rebuilding from the local cache alone), not merely no network. The container forms are the other way to move bytes without touching a registry. **Available in the public release** (S). The preview flag belongs to the commands that write: `add`, `install`, and `update` accept a dry-run flag. `outdated` takes none, because it never writes; passing the flag to it is refused outright. Every command above accepts repeatable registry addresses. (S) The usage lines below are a verbatim excerpt of the command listing: ```text $ law --help law add [@] [--project ] [--from ] [--registry ]… [--dry-run] [--json] law install [--project ] [--registry ]… [--offline] [--dry-run] [--recover] [--json] law update [@] [--project ] [--registry ]… [--dry-run] [--replay ] [--json] ``` Version selection: when no version is given, the newest stable dotted release wins. Prereleases and non-dotted releases are never selected automatically. The dotted grammar is a selection convenience, not a promise of compatible behavior. The full rules live on [Versions](/protocols/versions/). Argument handling is strict. Called with no package, `add` refuses — verbatim error-stream output, exit code 1: ```text $ law add law: REFUSAL: law add: needs exactly one argument — [@] ``` The refusal changes no file. Name exactly one package, with or without `@`, and run the command again. ## The lockfile is the memory The lockfile pins the whole closure: one exact version per package, content hashes for the compiled bytes, dependency edges, the effective semantics line, the canon profile block, and a resolution hash over all of it. Checks that run against a project resolve through the lock, not through the manifest wishes. Validation (pin coverage, hash agreement, profile agreement) is part of the same machinery. (S) See [**Packages, cases, and worlds**](/corpus/model/) for how the locked closure becomes a linked world, and the [Schema catalog](/protocols/schemas/) for the lock format itself. ## Registries are directories A registry is an address, not an authority. It is a directory layout of descriptors and blobs that the resolver reads. Descriptors name exact versions and content hashes. The downloaded bytes are verified against the promised hash before anything trusts them. Registry identity stays separate from mirror addresses, and a project may override addresses per registry id. (S) There is no hosted registry service behind these pages. Directory registries that the commands consume and publish are supported surface. Anything beyond that is **unconfirmed** (U): this guide promises nothing about it. ## Moving a closure without a network A portable container carries pins plus bytes for offline transfer. `add` accepts a container file in place of a package name, and `install` restores from one through its transfer flag. Neither form reads any registry. The installed result is the same pinned closure a registry resolution would have produced. (S) ## Canon profiles pin the jurisdiction set A canon profile pins a named set of packages (jurisdiction, discipline, or custom scope) with versions, namespaces, content hashes, calendars, and its own content hash. Three rules govern it: - The profile slot in the manifest is optional. A manifest that uses it names exactly one profile at the lock root. - The lock carries the profile block under the resolution hash. - Published profile descriptors repeat the full element list, so the graph is checkable before any download. (S) Profiles, release lines, and upgrades are covered from the release side on [**Publishing a release**](/corpus/releases/publishing/), [**Compatibility: versions and language lines**](/corpus/releases/compatibility/), and [**Upgrades and replay**](/corpus/releases/replay-and-upgrades/). ## Reproduction details
Toolchain, hashes, and full transcripts The example comes from the lab's [wiring replay](/corpus/lab/solutions/#how-the-fixtures-were-wired), run on the public `law-v0.1.1` release; the toolchain block is on the [topic index](/corpus/#reproduction-details). The full recorded step, verbatim, with the preparation commands and the two follow-up checks: ```shell python3 -c "p='/tmp/lab-work/wire/reg/law.toml'; t=open(p).read(); open(p,'w').write(t.split('[dependencies]')[0].rstrip()+'\n')" rm -f /tmp/lab-work/wire/reg/law.lock law add 'labparcels.iface@0.1.0' --project /tmp/lab-work/wire/reg --registry lab=/tmp/lab-work/registry --from lab law fix imports /tmp/lab-work/wire/reg --check law test /tmp/lab-work/wire/reg | tail -n 1 ``` ```text lab ← /tmp/lab-work/registry (--registry) exports (7): Owner, Parcel, ParcelKind, commercial, garden, owns, residential changed: .law/transport.json changed: deps/labparcels.iface.lawir.json changed: law.lock changed: law.toml /tmp/lab-work/wire/reg `use self` blocks are canonical total: 4 checked, 4 passed, 0 failed, 0 not run; code 0 ```
## Where to go next - [**Packages, cases, and worlds**](/corpus/model/) — what the closure is for: the linked world. - [**Package boundaries: what the corpus answers**](/corpus/package-boundaries/) — the import surface and the limits of a corpus question. - [Packages and cases](/cli/packages-cases/) — the command reference this page summarizes.