From 22f90a152bd9a886e1e926d538603769b7dfb585 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 04:53:00 +0000 Subject: [PATCH 1/2] feat(broker): align reported version with product release line The broker previously reported its Cargo crate version (3.0.0) in health/session/telemetry payloads even when shipped inside agent-relay 6.x, making install debugging and telemetry confusing. - Add util::version::broker_version() that reads AGENT_RELAY_VERSION at compile time and falls back to CARGO_PKG_VERSION for dev builds. - Route all listen_api, telemetry, and swarm version reporting through the helper and expose --version on the CLI. - Pass AGENT_RELAY_VERSION through publish, build-broker-binary, and package-validation workflows (plus Cross.toml for aarch64 musl), and fail validation if the binary does not report the release version. Closes #904 https://claude.ai/code/session_01Cs6t4GfxUHbnptziCekJAR --- .github/workflows/build-broker-binary.yml | 19 ++++++++++ .github/workflows/package-validation.yml | 29 ++++++++++++++- .github/workflows/publish.yml | 13 +++++++ Cross.toml | 11 +++--- crates/broker/src/cli/mod.rs | 1 + crates/broker/src/listen_api.rs | 4 +-- crates/broker/src/swarm.rs | 2 +- crates/broker/src/telemetry.rs | 2 +- crates/broker/src/util/mod.rs | 1 + crates/broker/src/util/version.rs | 43 +++++++++++++++++++++++ 10 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 crates/broker/src/util/version.rs diff --git a/.github/workflows/build-broker-binary.yml b/.github/workflows/build-broker-binary.yml index 26d259a22..28c419106 100644 --- a/.github/workflows/build-broker-binary.yml +++ b/.github/workflows/build-broker-binary.yml @@ -69,9 +69,28 @@ jobs: # crates/broker/src/telemetry.rs reads and bakes into the binary. Unset # variable → telemetry ships disabled (acceptable for forks and # pre-release pipelines). + # + # `AGENT_RELAY_VERSION` is consumed by `option_env!` in + # crates/broker/src/util/version.rs. The standalone build is fed + # from the dispatch tag input (or the repo's package.json version + # for branch pushes) so the broker reports a release-line version + # rather than the Cargo crate version. env: AGENT_RELAY_POSTHOG_KEY: ${{ vars.POSTHOG_PROJECT_KEY }} run: | + # Resolve a release-line version for AGENT_RELAY_VERSION. Prefer the + # workflow_dispatch tag (e.g. "v6.2.2" → "6.2.2"); fall back to + # package.json on branch pushes. + TAG_INPUT="${{ inputs.tag }}" + if [[ -n "$TAG_INPUT" && "$TAG_INPUT" != "latest" ]]; then + VERSION="${TAG_INPUT#v}" + else + # `npm pkg get` is available on the default GitHub runner image; + # the value is quoted JSON, so strip the quotes. + VERSION="$(npm pkg get version | tr -d '"')" + fi + export AGENT_RELAY_VERSION="$VERSION" + echo "AGENT_RELAY_VERSION=$VERSION" if [[ "${{ matrix.target }}" == "aarch64-unknown-linux-musl" ]]; then RUSTFLAGS="-C target-feature=+crt-static" cross build --release --target ${{ matrix.target }} --bin agent-relay-broker aarch64-linux-gnu-strip target/${{ matrix.target }}/release/agent-relay-broker 2>/dev/null || true diff --git a/.github/workflows/package-validation.yml b/.github/workflows/package-validation.yml index 07b0af3d7..e50874a3e 100644 --- a/.github/workflows/package-validation.yml +++ b/.github/workflows/package-validation.yml @@ -266,7 +266,19 @@ jobs: run: npm run build - name: Build broker binary - run: cargo build --release --bin agent-relay-broker + # Pin the broker's reported version to the same release line as + # `agent-relay` / `@agent-relay/sdk`. `option_env!` in + # crates/broker/src/util/version.rs consumes this at compile time + # and falls back to `CARGO_PKG_VERSION` when unset. + env: + AGENT_RELAY_VERSION: ${{ needs.build.outputs.new_version }} + run: | + if [ -z "$AGENT_RELAY_VERSION" ]; then + AGENT_RELAY_VERSION="$(node -p "require('./package.json').version")" + export AGENT_RELAY_VERSION + fi + echo "Building broker with AGENT_RELAY_VERSION=$AGENT_RELAY_VERSION" + cargo build --release --bin agent-relay-broker - name: Verify broker binary run: | @@ -285,6 +297,21 @@ jobs: echo "STANDALONE_BROKER=$STANDALONE_BROKER" >> "$GITHUB_ENV" echo "Verified broker binary: $STANDALONE_BROKER" + - name: Verify broker version matches release version + # Acceptance criteria from #904: released broker binaries must + # report the same version as the `agent-relay` / `@agent-relay/sdk` + # release that shipped them. + run: | + EXPECTED_VERSION="$(node -p "require('./package.json').version")" + REPORTED="$(./target/release/agent-relay-broker --version | awk '{print $NF}')" + echo "expected: $EXPECTED_VERSION" + echo "reported: $REPORTED" + if [ "$EXPECTED_VERSION" != "$REPORTED" ]; then + echo "ERROR: broker --version reported '$REPORTED' but package.json is '$EXPECTED_VERSION'." >&2 + echo "AGENT_RELAY_VERSION did not propagate at compile time. See crates/broker/src/util/version.rs." >&2 + exit 1 + fi + - name: Build standalone binary run: | mkdir -p release-binaries diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ea6d1dead..9d2815da4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -75,6 +75,11 @@ jobs: # Build Rust broker binary for all platforms (needed by SDK's AgentRelayClient) build-broker: name: Build broker (${{ matrix.target }}) + # Depends on `build` so we can pass the shipped product version into the + # broker at compile time via `AGENT_RELAY_VERSION`. See + # crates/broker/src/util/version.rs — released binaries report this + # version through health/session/telemetry payloads. + needs: build runs-on: ${{ matrix.os }} if: github.event.inputs.package == 'all' || github.event.inputs.package == 'main' || github.event.inputs.package == 'cli-prerelease' || github.event.inputs.package == 'sdk' || github.event.inputs.package == 'sdk-py' strategy: @@ -141,8 +146,15 @@ jobs: # `option_env!` in crates/broker/src/telemetry.rs consumes at compile time. # Unset variable → telemetry ships disabled (acceptable for # forks / pre-release pipelines). + # + # `AGENT_RELAY_VERSION` is consumed by `option_env!` in + # crates/broker/src/util/version.rs and becomes the broker version + # reported through health/session/telemetry payloads. We pin it to + # the release-line version so a `6.2.x` artifact does not report + # the Cargo crate version (`3.0.0`). env: AGENT_RELAY_POSTHOG_KEY: ${{ vars.POSTHOG_PROJECT_KEY }} + AGENT_RELAY_VERSION: ${{ needs.build.outputs.new_version }} run: | if [[ "${{ matrix.target }}" == "aarch64-unknown-linux-musl" ]]; then RUSTFLAGS="-C target-feature=+crt-static" cross build --release --bin agent-relay-broker --target ${{ matrix.target }} @@ -156,6 +168,7 @@ jobs: shell: pwsh env: AGENT_RELAY_POSTHOG_KEY: ${{ vars.POSTHOG_PROJECT_KEY }} + AGENT_RELAY_VERSION: ${{ needs.build.outputs.new_version }} run: | $env:RUSTFLAGS = "-C target-feature=+crt-static" cargo build --release --bin agent-relay-broker --target ${{ matrix.target }} diff --git a/Cross.toml b/Cross.toml index 16d272174..31f012b8c 100644 --- a/Cross.toml +++ b/Cross.toml @@ -1,9 +1,10 @@ [build.env] -# Forward the PostHog write key from the host into the cross-compile -# container so `option_env!("AGENT_RELAY_POSTHOG_KEY")` in src/telemetry.rs -# resolves the same way it does for native cargo builds. Unset on forks / -# CI, set via secret on release pipelines. -passthrough = ["AGENT_RELAY_POSTHOG_KEY"] +# Forward the PostHog write key and product-release version from the host +# into the cross-compile container so `option_env!` in +# src/telemetry.rs and src/util/version.rs resolves the same way it does +# for native cargo builds. Unset on forks / CI, set via secret / +# release pipelines. +passthrough = ["AGENT_RELAY_POSTHOG_KEY", "AGENT_RELAY_VERSION"] [target.aarch64-unknown-linux-musl] image = "ghcr.io/cross-rs/aarch64-unknown-linux-musl:main" diff --git a/crates/broker/src/cli/mod.rs b/crates/broker/src/cli/mod.rs index eb7002909..1bfe0f144 100644 --- a/crates/broker/src/cli/mod.rs +++ b/crates/broker/src/cli/mod.rs @@ -14,6 +14,7 @@ pub(crate) mod command_parse; #[derive(Debug, Parser)] #[command(name = "agent-relay-broker")] #[command(about = "Agent relay broker and worker runtime")] +#[command(version = crate::util::version::BROKER_VERSION)] struct Cli { #[command(subcommand)] command: Commands, diff --git a/crates/broker/src/listen_api.rs b/crates/broker/src/listen_api.rs index d0e0bc591..30a525248 100644 --- a/crates/broker/src/listen_api.rs +++ b/crates/broker/src/listen_api.rs @@ -309,7 +309,7 @@ fn listen_api_router_with_auth( .filter(|value| !value.is_empty()), memberships: config.memberships, default_workspace_id: config.default_workspace_id, - broker_version: env!("CARGO_PKG_VERSION").to_string(), + broker_version: crate::util::version::broker_version().to_string(), persist: config.persist, started_at: std::time::Instant::now(), }; @@ -403,7 +403,7 @@ pub(crate) fn listen_api_health_payload( json!({ "status": status, "service": "agent-relay-listen", - "version": env!("CARGO_PKG_VERSION"), + "version": crate::util::version::broker_version(), "uptimeMs": 0, "workspaceId": workspace_id, "defaultWorkspaceId": default_workspace_id, diff --git a/crates/broker/src/swarm.rs b/crates/broker/src/swarm.rs index 304bcb51f..69e13b473 100644 --- a/crates/broker/src/swarm.rs +++ b/crates/broker/src/swarm.rs @@ -1437,7 +1437,7 @@ impl BrokerClient { "hello", json!({ "client_name": "broker-swarm", - "client_version": env!("CARGO_PKG_VERSION"), + "client_version": crate::util::version::broker_version(), }), ) .await?; diff --git a/crates/broker/src/telemetry.rs b/crates/broker/src/telemetry.rs index 9a51fff04..7f34d2ca0 100644 --- a/crates/broker/src/telemetry.rs +++ b/crates/broker/src/telemetry.rs @@ -442,7 +442,7 @@ impl TelemetryClient { // which component emitted the event. `agent_relay_version` is kept // as a back-compat alias that mirrors `broker_version` here. if let Some(obj) = props.as_object_mut() { - let broker_version = env!("CARGO_PKG_VERSION"); + let broker_version = crate::util::version::broker_version(); obj.insert("agent_relay_version".to_string(), json!(broker_version)); obj.insert("broker_version".to_string(), json!(broker_version)); if let Some(ref v) = self.cli_version { diff --git a/crates/broker/src/util/mod.rs b/crates/broker/src/util/mod.rs index 21af3dd2e..f12c053e0 100644 --- a/crates/broker/src/util/mod.rs +++ b/crates/broker/src/util/mod.rs @@ -1,2 +1,3 @@ pub(crate) mod ansi; pub(crate) mod terminal; +pub(crate) mod version; diff --git a/crates/broker/src/util/version.rs b/crates/broker/src/util/version.rs new file mode 100644 index 000000000..20b8506f3 --- /dev/null +++ b/crates/broker/src/util/version.rs @@ -0,0 +1,43 @@ +/// The broker version reported in health/session/telemetry payloads and +/// by `agent-relay-broker --version`. +/// +/// Released binaries are compiled with `AGENT_RELAY_VERSION` set to the +/// shipped `agent-relay` / `@agent-relay/sdk` product version so that +/// `broker_version` in telemetry, `/api/config`, and the health/session +/// responses matches the artifact users installed. Local `cargo build` +/// invocations have no such env var; in that case we fall back to the +/// Rust crate's `CARGO_PKG_VERSION`, which is a reasonable developer-build +/// label rather than a release-line identifier. +pub const BROKER_VERSION: &str = match option_env!("AGENT_RELAY_VERSION") { + Some(v) => v, + None => env!("CARGO_PKG_VERSION"), +}; + +/// Returns the broker version. Prefer this helper over `env!("CARGO_PKG_VERSION")` +/// so all components report the same release-line version. +pub fn broker_version() -> &'static str { + BROKER_VERSION +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn broker_version_is_non_empty() { + // Either AGENT_RELAY_VERSION (release builds) or CARGO_PKG_VERSION + // (developer builds) must produce a non-empty string. + assert!(!broker_version().is_empty()); + assert_eq!(broker_version(), BROKER_VERSION); + } + + #[test] + fn broker_version_matches_compile_time_env() { + // When AGENT_RELAY_VERSION is set at build time, the helper must + // surface that exact value rather than the Cargo crate version. + match option_env!("AGENT_RELAY_VERSION") { + Some(v) => assert_eq!(broker_version(), v), + None => assert_eq!(broker_version(), env!("CARGO_PKG_VERSION")), + } + } +} From 2dddbec9a643f7c34026b1afba60e8fc5b709c52 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 19 May 2026 04:58:15 +0000 Subject: [PATCH 2/2] fix: address coderabbit review on version handling - Drop dead `needs.build.outputs.new_version` reference in package-validation.yml; standalone-macos-smoke only `needs: changes`, so the expression was always empty. Read package.json directly. - Treat an empty `AGENT_RELAY_VERSION` env var as unset so a stray unresolved CI expression cannot surface as an empty broker version string; fall back to CARGO_PKG_VERSION instead. https://claude.ai/code/session_01Cs6t4GfxUHbnptziCekJAR --- .github/workflows/package-validation.yml | 8 ++------ crates/broker/src/util/version.rs | 17 +++++++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/package-validation.yml b/.github/workflows/package-validation.yml index e50874a3e..d0fe16dc0 100644 --- a/.github/workflows/package-validation.yml +++ b/.github/workflows/package-validation.yml @@ -270,13 +270,9 @@ jobs: # `agent-relay` / `@agent-relay/sdk`. `option_env!` in # crates/broker/src/util/version.rs consumes this at compile time # and falls back to `CARGO_PKG_VERSION` when unset. - env: - AGENT_RELAY_VERSION: ${{ needs.build.outputs.new_version }} run: | - if [ -z "$AGENT_RELAY_VERSION" ]; then - AGENT_RELAY_VERSION="$(node -p "require('./package.json').version")" - export AGENT_RELAY_VERSION - fi + AGENT_RELAY_VERSION="$(node -p "require('./package.json').version")" + export AGENT_RELAY_VERSION echo "Building broker with AGENT_RELAY_VERSION=$AGENT_RELAY_VERSION" cargo build --release --bin agent-relay-broker diff --git a/crates/broker/src/util/version.rs b/crates/broker/src/util/version.rs index 20b8506f3..a6711e687 100644 --- a/crates/broker/src/util/version.rs +++ b/crates/broker/src/util/version.rs @@ -9,8 +9,12 @@ /// Rust crate's `CARGO_PKG_VERSION`, which is a reasonable developer-build /// label rather than a release-line identifier. pub const BROKER_VERSION: &str = match option_env!("AGENT_RELAY_VERSION") { - Some(v) => v, - None => env!("CARGO_PKG_VERSION"), + // Treat an empty `AGENT_RELAY_VERSION` as unset — CI workflows that + // forward an unresolved expression (e.g. `${{ needs.build.outputs.new_version }}` + // before the upstream job ran) can leave the var defined but blank, + // which would otherwise surface as an empty broker version string. + Some(v) if !v.is_empty() => v, + _ => env!("CARGO_PKG_VERSION"), }; /// Returns the broker version. Prefer this helper over `env!("CARGO_PKG_VERSION")` @@ -33,11 +37,12 @@ mod tests { #[test] fn broker_version_matches_compile_time_env() { - // When AGENT_RELAY_VERSION is set at build time, the helper must - // surface that exact value rather than the Cargo crate version. + // When AGENT_RELAY_VERSION is set to a non-empty string at build + // time, the helper surfaces that exact value; empty or unset + // falls back to the Cargo crate version. match option_env!("AGENT_RELAY_VERSION") { - Some(v) => assert_eq!(broker_version(), v), - None => assert_eq!(broker_version(), env!("CARGO_PKG_VERSION")), + Some(v) if !v.is_empty() => assert_eq!(broker_version(), v), + _ => assert_eq!(broker_version(), env!("CARGO_PKG_VERSION")), } } }