Repository navigation
Blocking incompatibility when the CLI version doesn't match the tenant's #101
Description
What's missing
A tenant and a blue CLI can be on different versions and nothing stops the developer.
- The tenant's expected client version travels as
minimum_client_version, and the schema is explicit that it is decoration: "Operator-visible rollout floor. Capability negotiation, not this field, enforces whether a client may consume the document." (crates/gh-service/src/schema.rs:26-30).ensure_client_compatible(schema.rs:84-105) never reads it — there's a test asserting a999.0.0floor still passes (schema.rs:562-569). - What does fail is
contract_version/required_capabilities, and it fails badly for a human:governance requires unsupported client capabilities: <slug>, <slug>names capability strings, not a version, and offers no way out. - Nothing covers a client newer than the tenant. Discovery carries only a document
version: 1, no deployment version (crates/gh-service/src/discovery.rs:6-18; server sideservices/control-api/src/lib.rs:1970-1988). - The server already knows: every client posts
client_version,platformandarchitectureon status (crates/gh-cli/src/commands.rs:1892-1894) and the dashboard renders them (control-api/src/lib.rs:5644,:5786). It just never acts on the mismatch.
So a developer on the wrong CLI either hits a cryptic capability error or silently drifts against the tenant's config.
Desired behaviour
- The tenant publishes the CLI version it supports (exact pin, or floor + ceiling).
- On config fetch — and ideally at first-run discovery — the client compares its own
CARGO_PKG_VERSIONand, on mismatch, hard-blocks. No governed launch, no passthrough, no falling back to a stale cache. This is a blocking incompatibility, not a warning. - Blocking is symmetric: a client newer than the tenant is blocked the same as an older one. Downgrading is a legitimate resolution.
- The block message names both versions and offers exactly two exits:
- Upgrade / downgrade to the required version — with a copy-paste command for the platform the client is actually running on.
blue resetto detach from the tenant (commands.rs:361-387,detach_tenantat:326). Detach must keep working while blocked.
Remediation text, per platform
Point at the release page for the required version, https://github.com/BlocksOrg/blue/releases/tag/v<required>, and print one pinned command — the one matching the running platform, not the whole matrix. Both installers already accept a pin via BLUE_VERSION (scripts/install.sh:6, scripts/install.ps1:5), so no new installer flags are needed.
macOS / Linux:
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/BlocksOrg/blue/releases/download/v<required>/install.sh | BLUE_VERSION=v<required> shWindows PowerShell:
$env:BLUE_VERSION = "v<required>"
irm https://github.com/BlocksOrg/blue/releases/download/v<required>/install.ps1 | iexFor anyone who'd rather grab the archive directly, name the one asset for their target: blue-v<required>-<target>.tar.gz (.zip on Windows), where <target> is one of the six published in .github/workflows/release.yml:57-75 — aarch64-apple-darwin, x86_64-apple-darwin, x86_64-unknown-linux-musl, aarch64-unknown-linux-musl, x86_64-pc-windows-msvc, aarch64-pc-windows-msvc. The client can resolve that from std::env::consts::OS / ARCH using the same mapping the installers use (install.sh:18-33, install.ps1:8-12).
Where it hooks in
- Wire field — either promote
minimum_client_versionto enforced and add a ceiling, or add a dedicated enforced constraint:crates/gh-service/src/schema.rs:14-60, contractdeploy/contract/governance.openapi.yaml:1663, docsapps/docs/next/reference/governance-config.mdx:81andapps/docs/next/deployment/blue-yaml.mdx:295. Ifminimum_client_versionchanges meaning, that's a contract change — the current wording promises the opposite. - The check — extend
ensure_client_compatible(schema.rs:84-105). It's already called on every fetch path (gh-service/src/client.rs:222,source.rs:154,source.rs:182), so a hard error there fails closed for free. - The message —
ensure_client_compatiblereturnsResult<(), String>; rendering a platform-specific remediation plus theblue resethint wants something richer.HarnessCompatibilityError::with_install_hint(commands.rs:2200) already does exactly this job for harness versions — worth mirroring. - First run — catching it in
discover_configuration(commands.rs:98-117) would block before OAuth instead of after, but that means putting a version in the unauthenticated discovery document. See open questions. - Dashboard — the client list already shows
client_version; flagging incompatible clients there is a natural follow-up.
Open questions
- Exact pin or floor + ceiling? A range is kinder to rolling upgrades; an exact pin is far easier to explain in the error message.
- Advertise the version pre-auth (discovery) as well as post-auth (governance-config)? Pre-auth gives a better first-run experience but exposes the deployment version to unauthenticated callers.
- Interaction with
required: trueand the offline/stale-cache path — a blocked client must not quietly serve the cached config.
Acceptance
- A mismatched CLI, older or newer, blocks every governed launch with one message naming the installed version and the required version.
- That message contains a runnable, version-pinned install command for the running platform, plus the release-page link.
blue resetis offered in the message and succeeds from the blocked state.- A matching CLI is unaffected; tests cover older / newer / in-range, and a golden message per published target.
Blue version: main @ 34abb7f (workspace 0.1.0)
Activity
tomi-stipancik commentedon Sep 16, 2026
@cube /codex /plan create an implementation plan. when we detect that the tenant is on a different version we should interactively ask the user to auto-install the correct blue cli version, similarly to how we ask users to install a policy compatible harness version
cubeorgdev commentedon Sep 16, 2026
Implementation plan ready.
It covers:
blue reset.After installation, users rerun their command so the old process cannot continue. No repository files changed.
View on dashboard
cubeorgdev commentedon Sep 16, 2026
User Message:
Implemented on
feat/tenant-client-version: exact version blocking, consent-based staged installation with rollback, cache protection, and updated docs/tests.Workspace build/tests, formatting, strict Clippy, and docs validation pass.
Native Windows/macOS execution remains unverified. Docker E2E was stopped for disk pressure.
Committed and pushed all changes; the working tree is clean. Created draft PR #130.
View on dashboard
cubeorgdev commentedon Sep 16, 2026
User Message:
Updated PR #130: deployment builds like
v0.1.0-rc.g02f8f34now require CLI0.1.0. Explicit tenant pins remain exact.Tests and checks passed; changes are pushed and the working tree is clean.
View on dashboard
cubeorgdev commentedon Sep 16, 2026
User Message:
Reverted the last commit and pushed to PR #130. Checks passed; working tree is clean.
Exact version matching is restored, but automatic RC/dev installation still needs enabling in the original installer.
View on dashboard
cubeorgdev commentedon Sep 16, 2026
User Message:
The job failed after the Windows runner lost communication with GitHub; its logs are unavailable. The Windows E2E job on the same commit passed, so no code defect is confirmed.
My rerun attempt returned 403: insufficient Actions permissions—please select Re-run failed jobs on the failed run.
View on dashboard