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 a 999.0.0 floor 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 side services/control-api/src/lib.rs:1970-1988).
- The server already knows: every client posts
client_version, platform and architecture on 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_VERSION and, 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 reset to detach from the tenant (commands.rs:361-387, detach_tenant at :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> sh
Windows PowerShell:
$env:BLUE_VERSION = "v<required>"
irm https://github.com/BlocksOrg/blue/releases/download/v<required>/install.ps1 | iex
For 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_version to enforced and add a ceiling, or add a dedicated enforced constraint: crates/gh-service/src/schema.rs:14-60, contract deploy/contract/governance.openapi.yaml:1663, docs apps/docs/next/reference/governance-config.mdx:81 and apps/docs/next/deployment/blue-yaml.mdx:295. If minimum_client_version changes 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_compatible returns Result<(), String>; rendering a platform-specific remediation plus the blue reset hint 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: true and 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 reset is 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)
What's missing
A tenant and a
blueCLI can be on different versions and nothing stops the developer.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).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.version: 1, no deployment version (crates/gh-service/src/discovery.rs:6-18; server sideservices/control-api/src/lib.rs:1970-1988).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
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.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 viaBLUE_VERSION(scripts/install.sh:6,scripts/install.ps1:5), so no new installer flags are needed.macOS / Linux:
Windows PowerShell:
For anyone who'd rather grab the archive directly, name the one asset for their target:
blue-v<required>-<target>.tar.gz(.zipon 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 fromstd::env::consts::OS/ARCHusing the same mapping the installers use (install.sh:18-33,install.ps1:8-12).Where it hooks in
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.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.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.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.client_version; flagging incompatible clients there is a natural follow-up.Open questions
required: trueand the offline/stale-cache path — a blocked client must not quietly serve the cached config.Acceptance
blue resetis offered in the message and succeeds from the blocked state.Blue version:
main@ 34abb7f (workspace0.1.0)