# Upgrades and compatibility ## Goal Move a deployment from one pinned state to another — runtime, canon packages, configuration, or data formats — with the change reviewed before traffic switches and the prior state kept bootable. ## Scope Component `law` CLI + pinned world (`law.toml`, `law.lock`, `.arxo` bundle, serve configuration), tool `0.1.1`. Scenario: a planned upgrade with a staging host and a production host. All versions and paths are synthetic created-examples. ## Prerequisites - The current production pin set recorded: `law version --json` output, the production `law.lock`, the production bundle filename + hashes, and the serve invocation (world dir, journal dir, port). - A staging host that can reach the registries (for `update`) or holds the new bundle (for offline hosts). - A replay corpus: saved `law ask --out ` evaluation directories and/or a decision journal for the current world. ## Steps 1. Classify the change and upgrade each layer separately, in this order (operator-policy-example order; the tool does not enforce it): (a) configuration, (b) canon packages (pins), (c) data formats, (d) runtime binary. Never move two layers in one switch. 2. Canon packages: on staging, inspect drift without touching the lock (real): `law outdated --project ` — "what is pinned, what is published, whether the pin bytes match; the lock is untouched". 3. Apply the pin move as a preview first (real flags): `law update [@] --project --dry-run`, then with `--replay ` to replay saved evaluations against the new closure. 4. Rebuild and re-verify the bundle (real): `law pack --project --out staging-N.arxo`, then `law inspect staging-N.arxo --verify --trust `. 5. Run the package's declared scenarios over the new lock world (real): `law test` (flags: `--family`, `--file`, `--grep`, `--json`). 6. Expected-change review: diff old vs new evaluations (real): `law case diff ` over `ask --out` directories (or `.arxo` answer sets). Every diff hunk must map to an intended normative change in the change record; an unmapped hunk blocks the switch. 7. Runtime binary: install the new `law` on staging only, re-run steps 5–6, and compare `law version --json` (`tool`, `semantics`, `std`, `binaryHash`) old vs new in the record. 8. Pre-switch checks on production: confirm the current journal is durable, the current bundle is intact (`law inspect --verify`), and `GET /readyz` is `200`. 9. Switch serve traffic by repointing the world symlink and letting the server follow it (real flag): run `law serve` with `--watch-world`, which "watches the `--world` symlink target change and warms the new world"; then flip the symlink. Alternatively stop, repoint, and start (simpler; brief downtime). Either way the `--journal` directory does not change: the journal is shared across generations and new records simply carry the new pin. Mixed-pin journals are the normal post-switch state, and rollback selects records by pin (see [Rollback](/operate/rollback/)). 10. Keep the prior bundle, prior `law.lock`, and prior binary path untouched on the host until the new state is accepted (see Result check). Do not garbage-collect during the observation window. ## Expected result - Staging `law test` green; `law case diff` contains only intended, recorded hunks. - After the switch, `GET /v1/world` (real endpoint: the active pin) reports the new pin, and `GET /readyz` returns `200`. - Prior bundle + lock + binary still present on disk. ## Result check - Historical replay: re-run the replay corpus over the saved data (`law eval ` for saved calculations; `law replay-record --world --journal --decision ` for journal entries, checking `resultHash`/`outcomeHash` per entry) and confirm the outcomes equal the staging-reviewed outcomes. This proves the saved state still reproduces — not that production serves it. Records are pin-checked against the loaded world, so replay each entry against the world its pin names — and under the executor the backup replay map names for its (pin, executor) group (see [Backup and restore](/operate/backup-restore/)): the tool does not verify executor compatibility itself, so a runtime move without a tested compatibility statement keeps the old binary as the replay executor for pre-move records — same pin, different group. - Deployment check: send the control question through the real production ingress with the pre-approved new pin and compare the served decision (pin, `resultHash`, outcome) with the staging-reviewed expectation. Only this proves which generation actually answers the external address after the switch. - Fresh-key canary (mandatory after any runtime move): the control question MUST use a new `Idempotency-Key` per run — resending a previously used key replays the journaled decision instead of exercising the new executable, so a fixed smoke key would "pass" without touching the new compute path. Confirm the new record's `engine` identity (version + `binaryDigest`) names the deployed runtime. The same-key repeat is a separate check that proves idempotent replay (same `decisionId`, no second record), never fresh computation. - Acceptance checkpoint (operator-policy-example): N consecutive clean hours (created-example: 24) with zero unexpected diffs before the prior bundle may be archived. ## Failures and diagnostics - `law update` without `--dry-run` surprises: re-read the help — the preview flags exist precisely for this; restore `law.lock` from version control and redo as `--dry-run` + `--replay`. - New `law test` failures after a runtime-only move: the binary changed semantics-sensitive behavior; keep the old binary path and treat this as a runtime regression, not a canon problem. - `/readyz` not `200` after a `--watch-world` flip: the new world did not warm on all workers; flip the symlink back (see [Rollback](/operate/rollback/)) and inspect server stderr / `--log-socket` output. - `--replay` mismatches with no `law case diff` mapping: stop; the closure moved further than the change record claims. ## Support boundaries - Supported: per-layer preview and diff commands named above; symlink world switch with `--watch-world`. - Reference setting: the layer order, observation window, and acceptance checkpoint are operator-policy-examples, not tool defaults. - Out of scope: database migrations for host-side mutable data and multi-host rolling upgrades are not tool behavior; see rollback limits in [Rollback](/operate/rollback/). ## Next step After acceptance, archive (do not delete) the prior bundle with its hashes; if the switch must be undone, follow [Rollback](/operate/rollback/).