Skip to content

fix(broker): forward harness to relaycast backend (SDK 2.3.0)#1069

Merged
willwashburn merged 1 commit into
mainfrom
fix/forward-harness-to-relaycast
Jun 9, 2026
Merged

fix(broker): forward harness to relaycast backend (SDK 2.3.0)#1069
willwashburn merged 1 commit into
mainfrom
fix/forward-harness-to-relaycast

Conversation

@willwashburn

Copy link
Copy Markdown
Member

Problem

Server-side telemetry on the relaycast backend (PostHog project 296966) records harness: "unknown" for 100% of relaycast_server_* events — verified live. The broker already detects the orchestrator harness (claude-code / codex / cursor / …) for its own PostHog events, but that value was siloed in TelemetryClient and never forwarded to the relaycast SDK. The engine reads the harness from the X-Relaycast-Harness header (HTTP) or ?harness= query (WS) and defaults to "unknown" when absent — and the broker never sent either.

The origin fields (@relaycast/sdk-rust, version) arrive because the SDK sends X-Relaycast-Origin-* unconditionally; the harness is conditional on with_harness() being called, which the broker never did.

Why the SDK bump

with_harness() was added in relaycast #161 and is only in published SDK ≥ 2.3.0. The broker pinned =2.0.0. Bumping =2.0.0 → =2.3.0 also pulls in the v8 event contract the gateway already emits — which is a net fix, not just a tax: at 2.0.0 the broker only recognized the pre-v8 reaction.added / agent.online types, while the deployed gateway sends message.reacted / agent.status.*. So reactions and presence were being silently dropped by the broker. Confirmed against the engine's emit sites (routes/reaction.jsmessage.reacted, presence adapter → agent.status.active/offline).

Changes

  • Bump relaycast SDK =2.0.0 → =2.3.0.
  • Cache the detected harness in telemetry::orchestrator_harness{,_opt}() (process-tree detection walks parent PIDs — resolve once) and reuse it for both our PostHog events and the forwarded value.
  • Forward the harness at all three relaycast client sites: the WS handshake (WsClientOptions::with_harness?harness=) and both HTTP build_relay_client helpers (RelayCastOptions::with_harnessX-Relaycast-Harness).
  • Inject AGENT_RELAY_HARNESS into spawned-agent env via spawn_env_vars, so JS-SDK agents (@relaycast/sdk resolves harness from env with no process-tree fallback) report it too. Threaded as a parameter for deterministic unit tests.
  • Update 3 stale bridge fixtures to the v8 event names the gateway actually emits (message.reacted + action, agent.status.active), and fix drops_reaction_without_channel to exercise the no-channel drop rather than a now-unknown event type.

Coverage by client

Client Share Fix
@relaycast/sdk-rust (Rust broker) ~77% with_harness() on WS + 2× HTTP client builders
@relaycast/sdk (JS spawned agents) ~23% AGENT_RELAY_HARNESS injected into spawn env

Tests

  • 701 broker lib tests pass (+2 new: spawn_env_vars_forwards_harness_when_present / _omits_harness_when_absent).
  • cargo clippy clean; cargo fmt --check clean.
  • The bump's full blast radius was exactly the 3 stale fixtures (out of 700) — no other contract drift.

Rollout

Takes effect once a broker release ships and users upgrade. After that, re-querying project 296966 should show claude-code / codex / etc. instead of unknown.

🤖 Generated with Claude Code

Server-side telemetry on the relaycast backend recorded `harness:
"unknown"` for 100% of events, even though the broker already detects the
orchestrator harness (claude-code / codex / cursor / …) for its own
PostHog events. The detected value was siloed in TelemetryClient and never
forwarded to the relaycast SDK, so the `X-Relaycast-Harness` header (HTTP)
and `?harness=` query (WS) were never sent and the backend defaulted to
"unknown".

Forwarding requires SDK >= 2.3.0 (`with_harness` was added in relaycast
#161, published as 2.3.0). Bumping `=2.0.0` -> `=2.3.0` also pulls in the
v8 event contract the gateway already emits, which is a net fix: the
broker at 2.0.0 only recognized the pre-v8 `reaction.added` /
`agent.online` event types, while the deployed gateway sends
`message.reacted` / `agent.status.*` — so reactions and presence were
being silently dropped.

Changes:
- Bump `relaycast` SDK `=2.0.0` -> `=2.3.0`.
- Cache the detected harness in `telemetry::orchestrator_harness{,_opt}()`
  (process-tree detection walks parent PIDs; resolve once) and reuse it
  for both our PostHog events and the forwarded value.
- Forward the harness at all three relaycast client sites: the WS handshake
  (`WsClientOptions::with_harness`) and both HTTP `build_relay_client`
  helpers (`RelayCastOptions::with_harness`).
- Inject `AGENT_RELAY_HARNESS` into spawned-agent env via `spawn_env_vars`
  so JS-SDK agents (`@relaycast/sdk`, env-only harness resolution, no
  process-tree fallback) report it too. Threaded as a parameter for
  deterministic tests.
- Update 3 stale bridge fixtures to the v8 event names the gateway emits
  (`message.reacted` with `action`, `agent.status.active`); fix
  `drops_reaction_without_channel` to exercise the no-channel drop rather
  than an unknown-type drop.

Tests: 701 broker lib tests pass (+2 new harness env tests); clippy clean;
fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@willwashburn willwashburn requested a review from khaliqgant as a code owner June 9, 2026 19:07
@codeant-ai

codeant-ai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Your free trial PR review limit of 300 PRs has been reached. Please upgrade your plan to continue using CodeAnt AI.

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Relaycast dependency upgraded to 2.3.0. Orchestrator harness is now detected once per process via cached telemetry functions and propagated through relay client configuration, agent environment variables, and telemetry initialization. Test fixtures updated for Relaycast 2.x presence and reaction event shapes.

Changes

Orchestrator Harness Detection and Propagation

Layer / File(s) Summary
Telemetry harness foundation and dependency update
crates/broker/Cargo.toml, crates/broker/src/telemetry.rs
Relaycast bumped to 2.3.0. Cached process-wide functions orchestrator_harness() and orchestrator_harness_opt() detect harness from explicit env override or parent-process chain, initialized once via OnceLock.
Relay client harness configuration
crates/broker/src/relaycast/auth.rs, crates/broker/src/relaycast/ws.rs, crates/broker/src/telemetry.rs
WebSocket and Relaycast service clients conditionally apply orchestrator harness via .with_harness(...) when available. Telemetry client initialization uses the cached harness value.
Agent environment variable harness propagation
crates/broker/src/spawner.rs
spawn_env_vars now accepts optional harness parameter and injects AGENT_RELAY_HARNESS environment variable when provided. Unit tests verify presence when supplied and absence when omitted.
Integration in spawn action
crates/broker/src/wrap.rs
The spawn broker action now wires orchestrator_harness_opt() into the spawn_env_vars call when preparing child agent environment variables.
Test fixtures for Relaycast 2.x contracts
crates/broker/src/relaycast/bridge.rs
Presence test fixture switched from agent.online to agent.status.active. Reaction events unified from reaction.added/reaction.removed to message.reacted with action field. All existing assertions preserved under new contract shapes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • khaliqgant

Poem

A harness threads through every node,
Once cached, then carried down the road.
Relay and agent dance in sync,
With telemetry's orchestral link. 🐰

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: forwarding harness to relaycast backend with SDK 2.3.0 bump.
Description check ✅ Passed The description comprehensively covers the problem, solution, changes, test coverage, and rollout plan; it fully satisfies the template requirements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/forward-harness-to-relaycast

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 02dec50a9f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/broker/src/wrap.rs
&channels,
Some(&child_workspaces_json),
default_workspace_id.as_deref(),
crate::telemetry::orchestrator_harness_opt(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Derive spawned agents' harness from their CLI

For spawn actions where the requested child CLI differs from the broker's parent harness (for example a Codex-driven broker spawning claude, or a broker launched from a service with no detectable parent spawning codex), this passes the broker's orchestrator harness or None into the child's environment. The JS SDK treats AGENT_RELAY_HARNESS as the spawned process's request harness, so those child Relaycast events are now misattributed to the parent or remain unknown even though params.cli identifies the actual child harness.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/broker/src/telemetry.rs`:
- Around line 440-457: The detector currently cached by orchestrator_harness()
doesn't consider the AGENT_RELAY_HARNESS environment override, so
orchestrator_harness_opt() can drop a valid explicit value; update the detection
path (the detect_orchestrator_harness function called by orchestrator_harness())
to check for AGENT_RELAY_HARNESS first (like any other explicit env override)
and return that value when present so the cached String includes the override
and orchestrator_harness_opt() will preserve it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f8f9796e-f7ab-40b6-8ccd-da0d5f77ed0b

📥 Commits

Reviewing files that changed from the base of the PR and between 6b67acf and 02dec50.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • crates/broker/Cargo.toml
  • crates/broker/src/relaycast/auth.rs
  • crates/broker/src/relaycast/bridge.rs
  • crates/broker/src/relaycast/ws.rs
  • crates/broker/src/spawner.rs
  • crates/broker/src/telemetry.rs
  • crates/broker/src/wrap.rs

Comment on lines +440 to +457
/// Process-wide cached orchestrator harness: explicit env override, else
/// process-tree detection (claude-code / codex / cursor / …). Detection walks
/// the parent-process chain, so we resolve it once and reuse the result for
/// both our own PostHog events and the harness we forward to the relaycast
/// backend. Returns the [`UNKNOWN_ORCHESTRATOR_HARNESS`] sentinel when
/// undetectable.
pub(crate) fn orchestrator_harness() -> &'static str {
static CACHE: std::sync::OnceLock<String> = std::sync::OnceLock::new();
CACHE.get_or_init(detect_orchestrator_harness)
}

/// Like [`orchestrator_harness`] but `None` instead of the `"unknown"`
/// sentinel, so callers can skip forwarding a non-informative value (the
/// relaycast backend already defaults a missing harness to `"unknown"`).
pub(crate) fn orchestrator_harness_opt() -> Option<&'static str> {
let harness = orchestrator_harness();
(harness != UNKNOWN_ORCHESTRATOR_HARNESS).then_some(harness)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include AGENT_RELAY_HARNESS in broker-side harness detection.

orchestrator_harness_opt() now drives relaycast forwarding, but the detector path it wraps does not check AGENT_RELAY_HARNESS. If that key is the only explicit override, Line 455 resolves to inferred/"unknown" and drops the intended value.

Suggested patch
 fn detect_orchestrator_harness() -> String {
     for key in [
+        "AGENT_RELAY_HARNESS",
         ORCHESTRATOR_HARNESS_ENV,
         "RELAYCAST_HARNESS",
         "X_RELAYCAST_HARNESS",
     ] {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/broker/src/telemetry.rs` around lines 440 - 457, The detector
currently cached by orchestrator_harness() doesn't consider the
AGENT_RELAY_HARNESS environment override, so orchestrator_harness_opt() can drop
a valid explicit value; update the detection path (the
detect_orchestrator_harness function called by orchestrator_harness()) to check
for AGENT_RELAY_HARNESS first (like any other explicit env override) and return
that value when present so the cached String includes the override and
orchestrator_harness_opt() will preserve it.

@willwashburn willwashburn merged commit 7e9a44a into main Jun 9, 2026
38 checks passed
@willwashburn willwashburn deleted the fix/forward-harness-to-relaycast branch June 9, 2026 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant