Skip to content

Upgrading

Upgrading Engineering Runtime is replacing one file. There is no migration step, no database, no daemon to restart.

For a Homebrew install:

brew update
brew upgrade engineering-runtime
"$(brew --prefix)/bin/runtime" version
runtime bootstrap

Use the prefix-qualified binary for the first check so an older manual copy in ~/.local/bin or /usr/local/bin cannot hide a successful Homebrew upgrade.

For a script or manual install:

# same download + verify flow as a fresh install, then:
sudo install -m 0755 engineering-runtime-$VERSION-linux-$ARCH/runtime /usr/local/bin/runtime
runtime version
runtime bootstrap

See Installation for the per-platform download commands.

Your runtime carries its own contract

This site gets you installed, gets your first capabilities running, and explains how the pieces fit. Any Runtime command prepares the Home automatically; runtime bootstrap is the optional explicit report. Start at <Runtime Home>/RUNTIME-AGENT.md (the contract) and manifest.json (this binary's providers and operations). Specs and command cheatsheets for your installed version live in <Runtime Home>/specs/ and commands/. Read those for precise operational detail — they describe the binary you actually have. Come back here for concepts, upgrades, and anything not yet installed.

config diff policy is the shipped way to see allowed_binaries drift against this binary's compiled default — do not invent a throwaway Home to diff policy.

What changes and what survives

The next command you run after replacing the binary refreshes Runtime Home automatically: RUNTIME-AGENT.md, manifest.json, specs/ and commands/.

Path On upgrade
RUNTIME-AGENT.md, manifest.json, specs/, commands/ Replaced. Files the new version no longer ships are deleted from specs/ and commands/
version Updated to the new binary's version
config.yaml Never touched
policy-config.yaml Never touched
context.yaml Retired. If your Home still has one it now blocks every command — run runtime context retire, then move it out yourself
capabilities directory Never touched. Runtime ships no capabilities and adds none
logs/, cache/ Untouched

Upgrading an install from before 2026-08-11

Such a Home still holds the four documents Bootstrap used to seed. Nothing deletes, moves or rewrites them — but Runtime no longer owns that location, and one of them now needs a decision from you.

runtime config validate

reports them under Unowned state in Runtime Home, classified against every default this project has ever released:

Reported as What it means What to do
unmodified default … — safe to delete it matches this binary's compiled default deleting it preserves behaviour and lets the compiled default govern
unmodified default … — but not this binary's compiled default it is unchanged, but its release behaviour differs from 0.9.0 inspect runtime config validate / config diff policy; deleting it changes what governs
edited or unrecognised you changed it, or it came from a version we cannot recognise move it out — see below

An edited policy-config.yaml in the Runtime Home stops governing. Runtime will not guess whether you meant those rules to still apply from a location it has disclaimed, so it refuses and tells you the two ways out rather than picking one. Governed commands stop until you choose; runtime config validate keeps working, which is where the instructions are.

runtime config migrate-home --to ~/.config/engineering-runtime            # see the plan
runtime config migrate-home --to ~/.config/engineering-runtime --write    # copy it out
export RUNTIME_POLICY_FILE=~/.config/engineering-runtime/policy-config.yaml

The write copies. Your original is never deleted, renamed or modified, an existing destination is never overwritten, and --to must already exist. The copy is read back and checked before Runtime says it worked. Or delete the file to run on the compiled safety profile.

A policy your Control Plane delivered with portal sync is not affected — that is an active delivery, not something left behind, and it keeps governing.

Context is not migrated at all: kubectl, oc and gcloud own it, so Runtime shows you the equivalent native command instead of copying it somewhere new.

Your policy edits, your context definitions and your capabilities survive every upgrade. That is the guarantee the ownership split exists to provide.

The first command consumes the refresh

Bootstrap runs before every command. If you run runtime version first, a subsequent runtime bootstrap prints no refresh line — the refresh already happened. That is success, not failure.

Schema migrations

Replacing the binary does not rewrite config.yaml or policy-config.yaml. Those files are yours. A newer Runtime can still read a supported older shape: it migrates in memory, leaves the bytes on disk alone, and executes against the migrated meaning.

That is different from config migrate-home, which copies a leftover file out of the Runtime Home. Schema migration is about the document's version, not its location.

# what this binary understands (generated, not hand-written)
cat ~/.engineering-runtime/specs/schema-contract.json

# inspect; the file is not touched
runtime config migrate --surface config --file ~/.config/engineering-runtime/config.yaml
runtime config migrate --surface policy --file ~/.config/engineering-runtime/policy.yaml

# apply only after the dry-run digest is the file you meant
runtime config migrate --surface policy --file ~/.config/engineering-runtime/policy.yaml \
  --write --expect-digest <digest-from-dry-run>
Outcome What Runtime does
Supported, behaviour-preserving Reads and executes; file bytes unchanged unless you pass --write
Meaning-changing or below the executable floor Explains why; governed execution blocks; the file is not rewritten
Newer than this binary, or older than the parse window Fails with the observed version and the supported range
--write without --expect-digest, or a digest mismatch Refuses; a race between dry-run and write must not silently rewrite a different file
Path inside the Runtime Home Refused — Home is not where you keep documents you edit

There is no auto_migrate, no write during bootstrap, and no network step. The binary never reads this website to decide a migration.

In v0.9.4, config is schema v2 and policy is schema v3 — unchanged since v0.9.1, so upgrading needs no document edit. Config v1 migrates in memory without changing behaviour; v2 adds the local capabilities.authoring_source selector, which since v0.9.2 is optional: a single capabilities directory is authored into with no key set, and an existing selection still wins. Policy v3 adds command_policy.admitted_helpers; the v2→v3 migration is behaviour-preserving and rewrites nothing, so a v2 document is read unchanged. The policy v1→v2 migration removes the retired top-level enabled key: absent/true is behaviour-preserving and can run after an in-memory migration; false is meaning-changing and blocks until you remove it deliberately. The strict version-exact shapes are in specs/config-schema.json and specs/policy-schema.json.

The upgrade trap worth knowing

New policy defaults do not reach existing installs.

policy-config.yaml is user-owned, so a release that adds allowed_binaries entries or new deny rules changes nothing for anyone who already has the file. This has bitten real installs: a machine sat at 6 allowed binaries while the release shipped 21, and runtime command run helm was denied for what looked like no reason at all.

This is a deliberate trade — never clobbering your policy is worth more than auto-delivering defaults — but you have to know about it. The binary now surfaces the difference; it still will not rewrite the file.

Diagnose it

runtime config validate names the effective $RUNTIME_POLICY_FILE and, when its allowed_binaries differ from this release, prints a count:

Policy:  /company/runtime/policy.yaml (explicit override (RUNTIME_POLICY_FILE))
          info: allowed_binaries differs from runtime 0.9.1 defaults:
                15 release-default binaries absent; 0 policy-only binaries
                Run: runtime config diff policy

runtime config diff policy prints the full set — every release-default binary the effective policy omitted, and every policy-only binary this release does not ship. It is read-only. Version 1 compares allowed_binaries only.

runtime config validate
runtime config diff policy
runtime --output json config diff policy

A fresh Home with no RUNTIME_POLICY_FILE runs the compiled default and prints no drift summary. A verified enterprise generation is reported as provenance and is never compared with vendor defaults.

Do not bootstrap a throwaway Home to recover the shipped YAML: Bootstrap no longer writes policy. Start from the compiled default with runtime config init policy --output … if you want a file to edit.

Verify the upgrade

runtime version
runtime config validate

# a capability that validated before must still validate
runtime capability validate files/notes-roundtrip

Validation resolves every step against the provider surface of the installed binary, so re-validating your capabilities after an upgrade is the fastest way to find an operation that changed or was removed.

If you keep capabilities in a checked-out source, validate all of them:

CAPABILITY_SOURCE=~/work/team-capabilities
for f in $(find "$CAPABILITY_SOURCE" -name '*.md'); do
  runtime capability validate "$f" || echo "FAILED: $f"
done

Downgrading

Downgrading works and is symmetric: specs/ and commands/ refresh back down to what the older binary ships, because the refresh triggers on any version difference, not just a newer one. Your user-owned files are untouched in that direction too.

The one thing to check is capabilities. A capability written against an operation that only exists in the newer binary will fail validation after a downgrade — which is exactly the intended behaviour, caught before execution rather than during it.

Upgrading in CI

By default the setup action installs latest. Pin a tag only when the pipeline must stay reproducible, and bump that pin deliberately:

- uses: engineeringruntime/engineering-runtime-ci/.github/actions/setup-runtime@main
  with:
    # omit version → latest
    # version: v0.9.1   # pin any published tag for reproducible pipelines

CI jobs build a fresh Runtime Home every run, so the upgrade trap above does not apply there — with no selected document a new job uses the release's compiled defaults. That asymmetry is worth remembering when a pipeline works and a laptop doesn't.

See CI/CD.

Operational examples on this site were verified against Runtime 0.9.8. After bootstrap, the version-exact files in Runtime Home win.