Upgrade the Kit version
Move your Instance to a new Kit version, and converge it.
An Instance has one Kit version: the version of @jr2/orchestrator that it installs. package.json pins @jr2/orchestrator and @jr2/cli to that one exact version. To upgrade, you change the two pins to the new version, install, and run jr2 up. Each upgrade ends in jr2 up. It converges the cluster to the new version, and it updates the jr2 block in AGENTS.md, which your Assistant reads.
In the commands on this page, <version> is the new Kit version, such as 0.4.1, and <old version> is the version that the Instance has now.
Before you start
- An Instance that you made with
jr2 init, with its dependencies installed with npm. - The new version. To see the latest release, run
npm view @jr2/cli version. - If the Instance sets
kitRegistry, write access to that registry. See Mirror the Kit images.
See the Kit version now
Run this command in the Instance folder:
jr2 version --local…
cli: <old version> <instance folder>/node_modules/@jr2/cli
global: <global version> <global folder>
kit: <old version> <instance folder>/node_modules/@jr2/orchestrator ok
instance: my-instance <instance folder> (installed kit → ghcr.io/snapwich)
node: v24.18.0cliis the CLI that runs: the Instance's own@jr2/cli.globalis your globaljr2. In an Instance folder, it runs the Instance's own@jr2/cli. This is the Handoff. Thus the version of the globaljr2does not change what the Instance runs.kitis the Kit version.okmeans that the CLI and the Instance use the same copy of@jr2/orchestrator, and that this copy has the version of the CLI.
<global version> is the version of your global jr2. <instance folder> and <global folder> are paths on your computer, and node gives your version of Node.js. The first line (…) is the .env line, when .env sets a variable that your shell does not set.
Install the new version
Change the two pins in one command:
npm install --save-exact @jr2/orchestrator@<version> @jr2/cli@<version>npm keeps @jr2/orchestrator in dependencies and @jr2/cli in devDependencies. --save-exact writes the version with no range, as jr2 init does.
When the Instance also uses @jr2/machines, upgrade it in the same command. It must have the same version:
npm install --save-exact @jr2/orchestrator@<version> @jr2/cli@<version> @jr2/machines@<version>Caution
Do not upgrade one of the packages alone. If you upgrade only @jr2/orchestrator, npm shows npm warn ERESOLVE overriding peer dependency and installs it. The CLI and the Kit then have different versions, and each command that reads the Instance, other than jr2 version, refuses. See the Rules.
Then check the result:
jr2 version --local…
cli: <version> <instance folder>/node_modules/@jr2/cli
global: <global version> <global folder>
kit: <version> <instance folder>/node_modules/@jr2/orchestrator ok
instance: my-instance <instance folder> (installed kit → ghcr.io/snapwich)
node: v24.18.0The cli line and the kit line must show the same version, and the kit line must end in ok. When you upgraded only @jr2/orchestrator, the kit line gives the version of the jr2 that runs:
kit: <version> <instance folder>/node_modules/@jr2/orchestrator MISMATCH — this jr2 (<old version>) runs against <version> (<instance folder>/node_modules/@jr2/orchestrator)Then install the two packages again, with one version.
Your global jr2 can stay at its version. Its version matters only for jr2 init and for commands outside an Instance. In an Instance, it runs the Instance's own @jr2/cli. To upgrade it too, run npm i -g @jr2/cli@<version>.
Mirror the new images
Do this step only when the Instance sets kitRegistry. The mirror does not have the images of the new version yet, and jr2 up never pushes them. In the Instance folder, run:
jr2 kit push <registry><registry> is the value of kitRegistry. The Instance's own @jr2/cli runs, so the command copies the images of the new version. Mirror the Kit images shows its output.
Compare with the cluster
jr2 version, with no --local, also reads what the cluster runs. Before the converge, it tells you that the cluster is at the old version:
jr2 version…
orchestrator: <old version> assembly 87cddbde1271 ghcr.io/snapwich/jr2-orchestrator:<old version>
operator: <old version> ghcr.io/snapwich/jr2-operator:<old version>
…
skew: deployed <old version> predates this kit <version> — `jr2 up` converges it87cddbde1271 is the hash of the current Assembly, and your hash is different. The orchestrator line also gives the image of the Orchestrator that runs in the cluster.
When the Orchestrator in the cluster is newer than the Kit version of the Instance, the skew line says deployed <deployed version> is newer than this kit <version> — `jr2 up` would roll the orchestrator back; the operator is never downgraded. <deployed version> is the version in the cluster.
jr2 version does not refuse a Kit version mismatch. It shows MISMATCH on the kit line. When package.json pins a version that is not installed, the kit line ends in ; package.json pins <version> — reinstall.
Converge
jr2 up…
AGENTS.md: jr2 block updated for <version>
jr2 up — instance "my-instance" → context <context> / namespace my-instance
typecheck: tsc --noEmit (the instance's own compiler)
…
images: installed kit — the published v<version> Harness, operator and Orchestrator, pulled from ghcr.io/snapwich
…
operator: applying v<version> (ghcr.io/snapwich/jr2-operator:<version>) over v<old version>
…
orchestrator: applying (image ghcr.io/snapwich/jr2-orchestrator:<version>)
orchestrator: waiting for rollout
orchestrator: verified — 1 pod(s) running ghcr.io/snapwich/jr2-orchestrator:<version>
…
assembly: publishing 36b7afe19f88 (no current Assembly)
assembly: 36b7afe19f88 is current — workflows: <workflows>
converged — `jr2 run <workflow>` when ready<context> is the kubectl context of the cluster. 36b7afe19f88 is the hash of the Assembly for the new version, and your hash is different. The new Orchestrator does not load the Assembly of the old version, so the publish line says no current Assembly.
jr2 up does these things for the upgrade:
AGENTS.md. Before the cluster work, it writes the docs index of the new Kit version between the twojr2-docsmarkers. It changes nothing outside the markers. A converge that fails still updates the block.jr2 updoes not change anAGENTS.mdthat has no markers. When the markers are not a correct pair,jr2 updoes not change the file, and the converge continues:warning: AGENTS.md: <reason>, so the jr2 block was not updated.- The typecheck. It typechecks the Instance against the new Kit. A Workflow that the new version does not accept stops the converge before the cluster changes. See the Rules.
- The images. The nodes pull the Kit images at the tag of the new version, the image of the Orchestrator too. The pod of the Orchestrator starts again on it. A Sandbox Image does not change, because the runtime of jr2 is not in it.
- The Assembly. The Assembly records its Kit version, so the hash changes.
jr2 uppublishes the Assembly after the new Orchestrator runs. Until the publish, the new Orchestrator has no current Assembly, and it starts no run. - The operator. All the Instances on the cluster share one operator.
jr2 upupgrades an older operator, so the upgrade of one Instance upgrades the operator for each Instance on the cluster.jr2 upnever downgrades the operator. When a different Instance upgraded the operator to a later version,jr2 upkeeps it:operator: leaving v<deployed version> (newer than this kit's v<version> — never downgraded).
What happens to the runs
A live run holds the Assembly that it started under, and that Assembly is for the old Kit version. The new Orchestrator cannot load it. Thus the publish of the new Assembly marks each live run drifted, also a run that waits at a Gate. jr2 status <run id> gives the reason, with the hash of the Assembly and the two versions: workflow "<workflow>": Assembly <hash> was published for Kit version <old version>; this Orchestrator is <version>. To keep the result of a run, let it end before you converge, or cancel it. See Machine shape and drift.
A Sandbox that runs keeps its pod, with the Harness of the old version. jr2 up does not replace the pod, because the new pod would lose the commits that nobody pushed. A Sandbox that starts after the converge uses the new version.
Rules
jr2 uprefuses an Instance that does not typecheck against the new Kit. It builds nothing and applies nothing. The lines after the refusal are the errors of the compiler:refusing: this instance does not typecheck — nothing was built and nothing was appliedEach verb that reads the Instance, other than
jr2 version, refuses when the Instance and the CLI do not use the same copy of@jr2/orchestrator, or when that copy does not have the version of the CLI. The refusal gives the two copies, and its last line gives the fix:texterror: Kit version mismatch — an Instance has one (ADR-0056): @jr2/orchestrator resolves to <instance kit version> (<path>) this jr2 (<cli version>) runs against <cli kit version> (<path>) pin "@jr2/orchestrator" and "@jr2/cli" at ONE exact version in <instance folder>/package.json and reinstall<instance kit version>is the version of@jr2/orchestratorthat the Instance installs.<cli version>is the version of thejr2that runs, and<cli kit version>is the version of the@jr2/orchestratorbeside thatjr2. When the Instance has no@jr2/cliof its own, the last line isadd "@jr2/cli": "<instance kit version>" to devDependencies in <instance folder>/package.json and reinstall — the Instance's own jr2 then runs. When nothing is installed, the refusal says@jr2/orchestrator does not resolve from <instance folder>.
Read next
- Converge
- Durable runs
- ADR-0056: the Handoff, and one Kit version for each Instance.
- ADR-0072: the docs in the Kit, and the block in
AGENTS.md. - ADR-0069: an Assembly names its Kit version, and the Orchestrator refuses any other.
- ADR-0030: a run whose Machine has a different shape.
- Mirror the Kit images