Reliable upgrades
Coding CLIs ship weekly and providers add models monthly. halo upgrade turns each new release into an eval-gated pull request. It never merges, never publishes a release and never retags a ring: a human merges every PR, and the normal ring rollout takes it from there.
What it does, per candidate
Section titled “What it does, per candidate”- Discover. For CLIs, the npm
latestdist-tag of each harness pinned in the watched profile (for example@anthropic-ai/claude-code). For models, each configured provider’s model list, read through your gateway and filtered bymatchandknown. - Verify. A CLI version must resolve to hash-pinned install artifacts (sha256 or npm integrity) through the same resolver
halo release builduses. A version with no verified artifacts is reported but never pinned, becausehalodwould refuse those bytes. It is retried on the next check. - Pin on a branch. One scalar edit:
harnesses.<h>.versionin the profile, ormodels.<alias>.modelin the Gateway. - Gate. Run the eval suite with the candidate against the current pin as control.
- Decide. On ship or hold, open a PR whose title and body carry the verdict, the diff, the artifact count and the full scorecard. On block, no PR is opened. Either way the outcome is recorded in the state file, so a candidate is handled once.
Configure
Section titled “Configure”The config lives in a dot-dir the policy loader skips. Paths are relative to the policy dir.
profile: engineering-next # its harnesses.<name>.version pins are the candidatessuite: ../../evals/suites/upgrade-gate.yamlharnesses: [claude-code, codex, gemini-cli]models: - provider: orchestrator-openai url: https://ai.acme.example/v1/models api_key_env: HALO_EVAL_GATEWAY_TOKEN match: "^gpt-[0-9.]+-codex$" # RE2; only these ids are worth evaluating known: [gpt-5-codex] # already evaluated or deliberately skipped alias: codex-default # the candidate edits models.codex-default.model# base: main # PR base branch (default: repo default)# state: .halos/upgrade-state.json # defaultPoint profile at the -next profile your canary ring uses. An upgrade then lands in ring1 first and reaches GA only through the normal promotion PR.
Check once
Section titled “Check once”$ halo upgrade check --dry-run --policy-dir examples/acme-corp \ --config stub-upgrade.yaml --npm-registry http://127.0.0.1:8765KIND TARGET FROM TO ARTIFACTS GATE PR / NOTEcli claude-code 2.1.312 2.1.330 UNVERIFIED - artifacts not verified: release: resolve claude-code@2.1.330 artifacts: GET https://downloads.claude.ai/claude-code-releases/2.1.330/manifest.json: status 404cli codex 0.100.0 0.101.0 UNVERIFIED - artifacts not verified: release: resolve codex@0.101.0 artifacts: npm returned @openai/codex/0.101.0@0.101.0, want @openai/codex@0.101.0cli gemini-cli 0.35.0 0.36.0 UNVERIFIED - artifacts not verified: release: resolve gemini-cli@0.36.0 artifacts: npm returned @google/gemini-cli/0.36.0@0.36.0, want @google/gemini-cli@0.36.0model orchestrator-openai/codex-default gpt-5-codex gpt-5.5-codex - - dry runThis output came from the example repo run against a local stub npm registry and model list (stub-upgrade.yaml is the example config with the model URL pointed at the stub). The stub npm registry answers 2.1.330 for claude-code, 0.101.0 for codex and 0.36.0 for gemini-cli, and the stub model list returns gpt-5-codex, gpt-5.5-codex and gpt-4o, so only gpt-5.5-codex passes match. The output is pasted unedited. The artifacts are unverified because the stub serves no tarballs. In a real run, a CLI row shows its verified artifact count, and without --dry-run the GATE column fills in with the PR URL or blocked by the eval gate; no PR.
Run it continuously
Section titled “Run it continuously”$ halo upgrade watch --every 6h --policy-dir .Run it in CI on a schedule, or as a long-running job with gh authenticated: PRs are opened through the GitHub CLI. Flags: --config (default <policy-dir>/.halos/upgrade.yaml), --npm-registry (a mirror), --parallel (eval trials), --local (evals on the host, testing only).
Why this is safe to automate
Section titled “Why this is safe to automate”- Bytes before verdicts. Nothing is evaluated or pinned unless its install artifacts verify against a hash.
- Statistically honest gate. Paired per-task comparison with CIs, flaky tasks excluded, and grader errors force hold: see evals.
- Humans own the irreversible steps. The watcher writes a branch and a PR. Merging changes policy only; releases, ring pointers and promotion stay with the release flow.
Related: CLI upgrade A/B, model upgrade canary.