Markdown for LLMs
Upgrades and compatibility
The source Markdown for this article. Copy it into your assistant or download it as a text file.
# 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 <dir>` 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 <dir>` — "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 <name>[@<version>] --project <dir> --dry-run`, then with
`--replay <dir>` to replay saved evaluations against the new closure.
4. Rebuild and re-verify the bundle (real):
`law pack --project <dir> --out staging-N.arxo`, then
`law inspect staging-N.arxo --verify --trust <key>`.
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 <evaluation A> <evaluation B>` 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 <dir>` for saved calculations;
`law replay-record --world <dir> --journal <dir> --decision <id>`
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/).