# Compatibility Four matrices: which released versions work together, which operations each facade ships, where each facade runs, and what touches the network. Every matrix names its sources, so a version bump becomes a list of fields and declarations to re-check. ## Version matrix All four packages below are published versions; the SDK versions match the repository manifests, and the canon version matches the canon manifest inside each package. | SDK | Canon package | Canon (model) | |---|---|---| | `@arxo/law` 0.3.3 (TypeScript) | `@arxo/canon-bgb-fristen` 0.1.5 | `de.bgb.fristen` 0.1.0 | | `arxo` 0.2.0 (Python) | `arxo-canon-bgb-fristen` 0.1.1 | `de.bgb.fristen` 0.1.0 | ```text @arxo/law 0.3.3 facade manifest: name and version fields @arxo/canon-bgb-fristen 0.1.5 canon manifest: name and version fields arxo 0.2.0 pyproject: version field; needs-python >= 3.12 arxo-canon-bgb-fristen 0.1.1 pyproject: version field de.bgb.fristen 0.1.0 canon package-meta: name and version fields ``` The canon manifest pins the linked world by hash (`contentHash` over `world.lawir.json`, the package hash over `package.lawir.json`), and the lock file pins the `de.bgb.core` dependency, the calendar resource, and the resolution hash. The loading chapter shows both documents. One limit: the two facades do not produce byte-identical documents for the same question — replay with the facade that recorded the answer. ## Capability matrix "Yes" means the operation exists in the shipped API quoted on the facade pages; "no" means it is absent, with the workaround beside it. | Operation | TypeScript | Python | |---|---|---| | `open(spec, options)` | Yes | Yes, dict options | | `truth` | Yes | Yes | | `focused_truth` | `focusedTruth` | No — ask `truth` | | `why_not` | `whyNot` | `why_not` / `whyNot` | | `collect` | Yes, `?name` or `{args, free}` | Yes, `?name` or `{args, free, comprehension}` | | `deadline` (`calendar_op`) | Yes | Yes | | `calc` (`term`) | Yes | Yes | | `positions` | Yes | Yes | | `unfold` | Yes, sync, no case needed | No — read at author time | | `questions` manifest | Yes | No — read at author time | | `passport` / `inModel` | Yes | Yes, `in_model` / `inModel` | | `explain` | Yes, needs the host | Yes, needs the host | | `verbalize` | Yes, needs engine and pack | No | | `ask` (uniform request) | Yes, 7 kinds | Yes, 6 kinds (no `focused_truth`) | | `parseSpec` | Yes | Yes, `parse_spec` | ```text TS LawPackage surface canonical declarations: class LawPackage block TS AskRequest kinds canonical declarations: AskRequest type Python LawPackage package module: LawPackage methods ask/truth/why_not/collect/deadline/calc/positions Python ask kinds package module: ask dispatch (truth/why_not/collect/calendar_op/term/positions) ``` ## Environment matrix | Facade | Runs on | Engine | Notes | |---|---|---|---| | TypeScript, Node | Node 20 minimum, 24 LTS recommended | Packaged `law_wasm_core.wasm` | Full surface; checkout trees, cache, registry | | TypeScript, browser | Modern browsers | `wasm` bytes passed in | No Node-only imports; canons from the bundle, pins for Cache Storage | | TypeScript, serve mode | Any with `fetch` | None local — the server | `open` with `serve` returns `ServePackage` | | Python | Any OS with Python 3.12+ | Bundled `law_wasm_core.wasm` via `wasmtime` | No registry, remote, or router in this release | ```text Node floor facade manifest engines field; quickstart asks for Node 20+ Browser entry browser declarations: BrowserOpenOptions with wasm/canons/pins Python floor pyproject requires-python >= 3.12 Python engine engine module: bundled wasm path via wasmtime Serve mode facade declarations: ServePackage and connectServe ``` ## Network matrix After install, the default path is offline: installed canons answer from local bytes checked against their manifest hashes. | Path | Network? | Notes | |---|---|---| | TS `open`, installed canon | No | Bytes checked against `contentHash`, then parsed | | TS `open`, checkout tree | No | `local` root/map/packages | | TS `open`, disk cache | No | Keyed by content hash | | TS `open`, registry | Yes | Skipped with `offline: true` | | TS `explain` | Yes | Host `law_explain` over the carried document | | TS `verbalize` | No | Local; needs the full engine and a pack | | TS serve mode | Yes | Every question goes to the server | | TS router | Yes | Routes over HTTP | | Python `open` | No | Installed canon, checkout tree, or cache only | | Python `explain` | Yes | MCP client on first use; offline refuses | | Python queries | No | Local engine always | ```text TS sources open module: installed/tree/cache sources offline, registry unless offline TS canon check canon source module: digest-then-parse against package-meta contentHash TS explain LawPackage: host law_explain over the carried document Python sources open module: canon/tree/cache; registry fetch not in this release Python explain package module: host client on first use; offline raises TransportError ``` ## Probe record The examples on these pages are one probed scenario, run on the versions above: event on 6 March 2026, a duration of 14 calendar days, candidate end 20 March 2026, legal time 17 September 2026, time zone Europe/Berlin, counting policy `BGB_FRISTEN_TAG`. ```text collect frist_ende(frist, ?end) COMPUTED 2026-03-20 truth frist_ende(frist, 2026-03-20) COMPUTED TRUE_ONLY truth frist_ende(frist, ) COMPUTED NEITHER truth frist_ende(frist, 2026-03-20), partial case COMPUTED NEITHER why_not over the partial case 2 blockers, one per candidate rule sources on every answer above [] hashes on every answer above program, semantic, result ``` The partial case removes the duration fact; the two blockers name the premises each candidate rule was waiting for. Re-run this probe after any version move: the verdicts must match, and the program hash names the new build. ## How to read a cell Each matrix row ends in a pointer: a manifest field, a declaration block, or a module. When a version moves, open the pointer first and update the cell from what it says. For the worked case behind these pages, see the [quickstart](/guide/quickstart/) and the [deadline app](/build/start/). ## Verification record The example, the pins, the commands, and their observed results for the versions above: ```text example deadline-app 0.1.0 (29 files, sha256 in the Start chapter) pins @arxo/law 0.3.3, @arxo/canon-bgb-fristen 0.1.5, arxo 0.2.0, arxo-canon-bgb-fristen 0.1.1, model de.bgb.fristen@0.1.0, adapter 1.0.0 checked 2026-10-03, Node v20.20.2 and v24.21.0 (63/63 + typecheck clean on both; v24 reporter prints `ℹ` summaries), Python 3.13 ``` ```text $ cd docs/build/examples/deadline-app && npm test # tests 63, # pass 63, # fail 0 $ npm run typecheck # tsc --noEmit, clean $ python3 python/test_deadline.py 10/10 passed $ node docs/build/tools/check-fences.mjs fences checked: 53, verbatim: 39, allowlisted: 14, failures: 0 ``` The site build enforces the documentation half of this record: `content:strict` runs the prose and link gates, and `deadline-app:archive:check` fails the build when the published zip, sidecar, or release manifest drifts from the example sources. Before publishing, one more check compares the pages with a fresh run: ```text $ node docs/build/tools/check-release.mjs release: 10 passed, 0 failed ``` It re-runs the suite and asserts the archive, the manifest, the Start checksum and file count, every quoted test count, the fences, and the typecheck. Published bytes are immutable under their name: changed sources ship as a new package version with a new archive name, never as a silent rewrite. The strict HTTP run (`npm run test:http:strict` in the unpacked archive, where sockets listen) belongs to the same pre-publish pass. Re-run the probe above and these commands after any version move; if a verdict changes, the matrices on this page change with it.