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.
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.
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.