docs← Back to article

Markdown for LLMs

Rollback

The source Markdown for this article. Copy it into your assistant or download it as a text file.

Download this articlePlain text ↗
# Rollback

## Goal

Return a deployment to the last accepted state — runtime binary plus
pinned world — quickly, in a defined order, with explicit success
criteria and known limits.

## Scope

Component `law` CLI + serve deployment, tool `0.1.1`. Scenario: the new
state (after an upgrade switch per [Upgrades and compatibility](/operate/upgrades-compatibility/)) is
rejected during the observation window; the prior bundle, lock, and
binary were kept on the host. All identifiers below are synthetic
created-examples.

## Prerequisites

- The prior state intact on the host: prior `.arxo` bundle + hashes,
  prior `law.lock`, prior `law` binary path (or reinstallable pinned
  archive + `.sha256`).
- The rejected state's identity recorded: new bundle name, new pin
  (`GET /v1/world` output), new `law version --json`.
- The decision journal preserved. One journal directory is shared
  across generations: every record carries its world's pin, so
  entries from the prior window and the rejected window normally
  coexist in the same directory. Windows are distinguished by
  recorded pin and decision id, not by separate directories.

## Steps

1. Declare the rollback: record which state is rejected and why; freeze
   further switches (operator-policy-example: page the change owner —
   created-example process).
2. Stop intake on the rejected world: disable the ingress route,
   then stop `law serve` and confirm the PID is gone (no graceful
   drain exists — in-flight calls die; only completed decisions
   have records). Fencing the port alone does not stop writers.
3. Switch order — data-plane first, then control-plane
   (operator-policy-example order):
   (a) restore the prior world directory from the labeled backup
   set (`--world` accepts a directory or a `profile.json`, never a
   `.arxo` bundle directly), then repoint the `--world` symlink to
   it;
   (b) if the runtime binary also moved, restore the prior binary path
   — runtime and world roll back together, never the world alone
   under a rejected binary.
4. Verify integrity of the selected artifacts and configuration
   BEFORE starting anything (real): `law inspect <prior-bundle>
   --verify --trust <key>`. Do not rely on the runtime to refuse
   damaged artifacts implicitly — a rollback procedure must not
   start a state it has not checked.
5. Start the chosen runtime + world pair with external traffic
   still fenced off. Step 2 stopped the process, so no running
   watcher can warm the prior world for you — this recipe always
   restarts; a hot rollback that relies on `--watch-world` without
   stopping would be a different procedure.
6. Check readiness and identity on the fenced instance, in this
   order: `GET /readyz` is `200`, then `GET /v1/world` shows the
   prior pin. Re-run acceptance on the restored state (real):
   `law test` over the prior project, `law eval` over a saved
   calculation, and `law replay-record --world <prior> --journal <dir>
   --decision <id>` for the prior window's decision ids. Each record
   is pin-checked against the loaded world; a record from another
   generation reports `PIN_MISMATCH` instead of replaying silently.
   Finish with one synthetic control query through `POST /v1/ask`
   (real endpoint) and confirm the expected document.
7. Open ingress only after step 6 is fully green.
8. Fate of the rejected window's cases and records: keep the journal
   whole — do not delete rejected-window records and do not rewrite
   history (the journal has no rewrite command — implementation
   limit). Decisions served during the rejected window stay recorded
   with the rejected pin; re-ask any affected cases against the
   restored world explicitly (new journal entries, new decisions).
   Select records by recorded pin and decision id; there is no
   supported "split this journal into two directories" procedure.
9. Mutable-data compatibility: if host-side systems consumed rejected
   decisions (tickets, payments, notifications), reconcile them
   case-by-case from the preserved rejected-window records; the tool
   cannot retract served decisions (implementation limit, not a
   missing flag).

## Expected result

- `GET /v1/world` reports the prior pin; `/readyz` is `200`.
- `law test`, `law eval`, and `law replay-record` exit `0` on the
  restored state.
- The journal preserved whole; prior-window and rejected-window
  records remain distinguishable by recorded pin.

## Result check

- Pin check: served pin equals the recorded prior pin, not merely "an
  old pin".
- Replay check: prior-window journal replays cleanly
  (`resultHash`/`outcomeHash` match per entry).
- Diff check: `law case diff` between a pre-upgrade saved evaluation
  and a post-rollback fresh ask of the same case is empty.
- Intake check: one synthetic ask through `POST /v1/ask` (real
  endpoint) returns the expected document before real traffic resumes.

## Failures and diagnostics

- `/readyz` not `200` after the step 5 restart: the prior world
  did not warm — check the symlink target, bundle readability,
  and server stderr; serve was possibly started against a pruned
  path.
- `law replay-record` mismatch on the prior journal: the "prior" world
  is not byte-identical to the accepted one (re-pack drift or lock
  edit); restore from the labeled backup set ([Backup and restore](/operate/backup-restore/)).
- Prior binary missing: reinstall from the pinned archive + `.sha256`
  (per [Install and trust artifacts](/operate/install-trust-artifacts/)) rather than downgrading through
  a package manager's "latest".
- Records from several pins in one journal: normal after any
  `--watch-world` switch, not a fencing failure. Select by recorded
  pin and replay each record against the world that matches its pin.

## Support boundaries

- Supported: world + runtime rollback via kept bundles, locks, and
  binaries; per-entry replay verification.
- Implementation limits: no journal merge/rewrite/retract commands;
  no automatic rollback trigger — the switch is always an operator act.
- Rollback limits: rollback restores the answering state, not the
  consequences of rejected answers; downstream effects need
  case-by-case reconciliation. "Safe rollback" in this article means
  only "the defined order was followed and all checks are green".

## Next step

File the rollback report (rejected pin, cause, affected decisions,
reconciliation list) and re-plan the upgrade as a new change; do not
re-attempt the same switch without a new review.