# Loading and pinning An answer without a pinned version is not reproducible. This chapter says how a spec becomes bytes: the required form, the sources each facade tries in order, the hash check on installed canons, the lock file that pins dependencies, and the offline flag. ## The spec is always name and version ```ts export function parseSpec(spec: string): { name: string; version: string }; ``` ```python def parse_spec(spec: str | None) -> dict[str, str]: ``` Both facades split `name@version` and refuse anything else. There is no `latest` in reproducible code: the version in the spec selects the exact bytes that answer, and the answer's `program` hash names those bytes back. The [deadline app](/build/start/) keeps the spec in one place so a version change is a single deliberate edit. ## Installed canons are checked by hash An installed canon is trusted by its hash, not its name. Before the bytes are parsed, the facade digests them and compares the digest with `contentHash` in the canon's manifest; a mismatch raises `IntegrityError` with the expected and actual digests and the path. The TypeScript resolution tries the installed package whose name follows the dotted rule, then the published aliases: ```js export const CANON_ALIASES = { 'de.bgb.fristen': '@arxo/canon-bgb-fristen', 'kz.corpus.ogpovts': '@arxo/canon-ogpo-vts', 'unidroit.picc': '@arxo/canon-unidroit-picc', }; ``` The manifest of the periods canon names its own version, the linked world the engine runs, the calendar, and the hashes: ```json { "name": "de.bgb.fristen", "version": "0.1.0", "namespace": "urn:de:corpus:clir:bgb-fristen", "clir": "world.lawir.json", "calendar": "resources/de-feiertage-bundesweit.calendar.json", "contentHash": "sha256:07b0ccc7…", "packageContentHash": "sha256:4a78d774…" } ``` `contentHash` covers the linked world — this canon plus its compiled import — and is what `open` checks. `packageContentHash` covers the canon's own compiled model and is the hash a lock file pins. Python resolves the same way through its installed `arxo_canon_*` packages, with the same digest-then-parse check. ## The lock file pins the dependencies The canon ships a lock file with the exact pins it was linked against: the dependency edges, each dependency's content hash, the calendar resource with its hash, and the resolution hash of the whole. ```json { "lockVersion": "0.2", "languageSemantics": "law.core/0.2.0", "packages": [ { "name": "de.bgb.core", "version": "0.1.0", "contentHash": "sha256:35d07b51…" } ], "resources": [ { "id": "urn:de:corpus:clir:bgb-fristen#KALENDER_BUNDESWEIT", "contentHash": "sha256:aa310583…" } ], "resolutionHash": "sha256:3b8f07b7…" } ``` The manifest also lists where the compiled import lives inside the package (`imports/de-bgb-core.lawir.json`), so the linked world is complete without fetching anything. ## Checkout trees When the canon is not installed — while its author iterates on it — both facades accept a checkout tree through `local`: ```ts local?: { root?: string; map?: Record; packages?: Record; }; ``` ```python open("de.bgb.fristen@0.1.0", {"local": { "root": "/path/to/checkout", "map": {"de.bgb.fristen": {"clir": "world.lawir.json"}}, }}) ``` `packages` carries already-parsed models keyed by `name@version`; `root` with `map` points at files on disk, resolved against the root when relative. A model whose declared version differs from the spec is skipped, and resolution continues to the next source. In the browser entry, only the in-memory `packages` form exists. Resolution order in TypeScript is: installed canon, checkout tree, disk cache, then the public registry unless offline. Python tries the installed canon, the checkout tree, and the cache; registry fetch is not in the Python release. ## The offline flag ```ts open('de.bgb.fristen@0.1.0', { offline: true }); ``` ```python open("de.bgb.fristen@0.1.0", {"offline": True}) ``` Offline keeps the facade off the network: TypeScript skips the registry source, and Python refuses to create the host client, so `explain` raises `TransportError`. Installed canons and checkout trees keep working — offline never meant "no canons", only "no network". ## The program hash Every answer carries `hashes.program`: the digest of the canon build that answered. Keep it with the archived `document` and the answer is replayable: the same spec resolves the same build, and the replay chapter of the [deadline app](/build/start/) re-runs the stored query and compares the `result` hash. The checksum step separately verifies the stored bytes. Four different checks, never to be merged: the stored-document checksum ≠ the result-hash comparison ≠ byte-equality of old and new documents ≠ authentication of the whole capture. One limit: the two facades do not produce byte-identical documents for the same question, so a replay must use the same facade that recorded the answer. ## Contract - The spec is `name@version`; anything else is refused. - Installed bytes are digested and compared with the manifest hash before parsing; a mismatch is an `IntegrityError`, not an answer. - The lock file pins every dependency and resource by hash. - `local` loads a checkout tree or in-memory models; a version mismatch skips the entry. - `offline` disables the registry and the host client; local sources keep working. - `hashes.program` names the build that answered; archive it with the document.