Markdown for LLMs
Versioning
The source Markdown for this article. Copy it into your assistant or download it as a text file.
# Versioning An answer from the deadline example is reproducible only under the pins that produced it. This page names the four version axes, the checklist for moving any of them, and the rule for old captures: replay under old pins, recompute under new ones, never overwrite. ```text docs/build/examples/deadline-app ``` ## The four axes | Axis | Pinned where | Current value | |---|---|---| | SDK package | `package.json` | `@arxo/law 0.3.3`, `arxo 0.2.0` (Python) | | Canon package | `package.json` | `@arxo/canon-bgb-fristen 0.1.5` | | Canon model version | `openModel` in `src/runtime.ts` | `de.bgb.fristen@0.1.0` | | Adapter and capture format | `src/to-case.ts`, `src/capture.ts` | `ADAPTER_VERSION 1.0.0`, `deadline-app.capture/1` | Each axis moves for its own reason: - **SDK package.** New engine or facade behavior. Answers keep their statuses and values across compatible SDK releases, but bytes may move: replays stay within one SDK. - **Canon package.** New canon build: regenerated bindings, corrected metadata, repackaged engine files. The model version it carries may or may not move with it — check the package notes against the model pin. - **Canon model version.** New formalization: changed rules, changed questions. A model move can change values and statuses, not just bytes. - **Adapter and capture format.** New mapping from form fields to facts (`toFacts`, `toCaseInput`) or a new capture layout. The adapter version is stamped into every capture so a replay knows which mapping produced it. The worked case pins all four: the packages above, the model `de.bgb.fristen@0.1.0`, the adapter `1.0.0`, the capture format `deadline-app.capture/1`, the time zone `Europe/Berlin`, and the policy `urn:de:corpus:clir:bgb-fristen#BGB_FRISTEN_TAG`. ## What a capture records A capture under `CAPTURE_FORMAT deadline-app.capture/1` carries its pins with the answer, so a replay later still knows its terms: - the form: `eventDate`, `durationDays`, `candidateEnd`, `legalTime`; - the adapter version `ADAPTER_VERSION 1.0.0`; - the SDK and canon package versions; - the model version `de.bgb.fristen@0.1.0`, the time zone, and the policy; - the answer: statuses, proof identifiers, the three hashes, and the canonical document as `documentBase64` with its `sha256`. `verifyCaptureIntegrity` checks the bytes against the recorded hash before `replayCapture` trusts them; either step fails closed. ## Upgrade checklist 1. Change one axis at a time and note the old and new values. 2. Reinstall from the lockfile (`npm ci`) so the tree matches the pins. 3. Run the full suite: `npm test`, the typecheck, and the Python checks. Read every failure as a behavior statement before touching code. 4. Compare the worked case: collect must still compute `2026-03-20` with `COMPUTED`, truth must still answer `TRUE_ONLY`, the wrong-date and partial cases must still answer `NEITHER`, and `whyNot` must still return 2 blockers — unless the upgrade note says otherwise, in which case the note is the new expectation and the suite is updated first. 5. If bytes moved but values did not, refresh the snapshots from runs you have read, one SDK at a time. 6. If the adapter moved, bump `ADAPTER_VERSION`, keep the old mapping readable for old captures, and add a regression case for the changed mapping. 7. If the capture format moved, bump `CAPTURE_FORMAT`, keep `verifyCaptureIntegrity` strict on both formats, and never rewrite old files into the new layout in place. ## Replay old, recompute new - **Replay old.** To reproduce an old answer, restore its pins — SDK, canon package, model version, adapter — and run `replayCapture` (the `replay` command or the replay endpoint). Replay re-runs the stored query and compares the `result` hash; the checksum step separately verifies the stored document bytes. Neither step establishes byte-equality of the old and new documents, and neither checks the capture's metadata: restoring the recorded pins is the operator's job, because the code does not verify the installed environment against them. - **Recompute new.** To answer under new pins, run `evaluate` again and store a **new** capture file. Never overwrite the old file: the old capture remains the record of what the old pins answered. ```text old pins + old capture -> replayCapture (must match) new pins + same form -> evaluate (new file in captures/) ``` Cross-SDK replay is out of scope in both directions: a capture saved by TypeScript is replayed by TypeScript, and a capture saved by Python is replayed by Python. The statuses and values agree; the bytes do not. Keep the old pins installed next to the new ones (separate checkouts or separate machines) while old answers must stay reproducible during an upgrade window. ## Next - [Patterns: batch, offline, and questionnaire](/build/patterns/)