Skip to content

Make unreported node load explicit - #308

Merged
khaliqgant merged 3 commits into
mainfrom
fix/node-load-telemetry
Aug 7, 2026
Merged

Make unreported node load explicit#308
khaliqgant merged 3 commits into
mainfrom
fix/node-load-telemetry

Conversation

@barryollama

Copy link
Copy Markdown
Contributor

Summary

  • return load: null until a node/provider explicitly marks a bounded utilization value as measured
  • accept legacy numeric heartbeat values for compatibility, but do not trust their hard-coded zeroes
  • define max_agents: 0 consistently as unlimited, including mixed-provider aggregation
  • reject future-dated node/provider heartbeat timestamps as fresh
  • update API docs and TypeScript, Python, Rust, and Swift SDK models/heartbeat behavior

Root cause

All shipped node providers sent a literal load: 0; Relaycast persisted it, provider aggregation took the maximum (also starting at zero), GET /v1/nodes returned the number, and agent-relay fleet nodes printed it without further calculation. The value was a placeholder, not a measurement.

This change adds explicit load_reported provenance. Existing rows remain unreported during migration; no historical zero is backfilled as measured.

Semantics

  • load: normalized managed-agent capacity utilization in [0,1]
  • broker aggregate: maximum provider load, reported only when every constituent provider reports a genuine measurement
  • max_agents: 0: unlimited capacity
  • roster/agent counts are not used to derive load

Verification

  • engine: 536/536 tests
  • types: 163/163 tests
  • TypeScript SDK: 416/416 tests
  • Python node SDK: 17/17 tests
  • Rust parity: 43/43 tests
  • TypeScript engine/types/SDK builds and lint pass
  • Swift source build passes; Swift test runner is unavailable on this host because the toolchain cannot import XCTest

No Cloud dashboard changes. Do not merge or deploy without the Relaycast merge/deploy gates.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kjgbot, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c01a7a9e-20a1-4959-bd1c-553c78343df4

📥 Commits

Reviewing files that changed from the base of the PR and between 1a215d1 and 6942caa.

📒 Files selected for processing (1)
  • packages/sdk-rust/src/ws.rs
📝 Walkthrough

Walkthrough

Node load telemetry now distinguishes measured values from unavailable values. The engine persists explicit reporting state, treats max_agents: 0 as unlimited, rejects future heartbeats, updates placement ordering, and aligns wire schemas and SDKs.

Changes

Node load telemetry

Layer / File(s) Summary
Wire contract and SDK heartbeat semantics
openapi.yaml, packages/types/..., packages/sdk-*/*
Heartbeat schemas accept absent or nullable load values. Numeric loads are bounded to 0–1. SDK roster models expose optional load values. Provider and direct-node heartbeats no longer use placeholder load values.
Load state persistence and aggregation
packages/engine/src/db/..., packages/engine/src/engine/node.ts, packages/engine/src/engine/nodeProvider.ts, packages/engine/src/__tests__/conformance/*
The engine stores loadReported for nodes and providers. Lifecycle transitions clear the flag. Mixed-provider aggregation remains unlimited when any provider has max_agents: 0. Aggregate load is unavailable until all providers report measurements.
Heartbeat freshness and placement ordering
packages/engine/src/engine/placement.ts, packages/engine/src/engine/nodeProvider.ts, packages/engine/src/__tests__/conformance/nodeLiveness.test.ts
Node and provider liveness rejects missing, stale, and future timestamps. Placement prioritizes reported load, measured load, active-agent count, and name.
Telemetry contract documentation
README.md, CHANGELOG.md, packages/*/CHANGELOG.md, .agentworkforce/trajectories/...
Documentation records nullable load values, explicit measurement, and zero as the unlimited-capacity sentinel.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SDK
  participant FleetWire
  participant Engine
  participant Database
  participant Placement
  SDK->>FleetWire: Send heartbeat with optional load
  FleetWire->>Engine: Validate load and load_reported
  Engine->>Database: Persist load reporting and capacity state
  Engine->>Placement: Provide liveness and capacity data
  Placement->>Engine: Select node using capacity ordering
Loading

Possibly related PRs

Suggested labels: size:XL

Suggested reviewers: willwashburn

Poem

A rabbit checks each heartbeat’s trace,
And marks unknown load with null in place.
Zero means unlimited capacity,
Future beats lose validity.
Measured hops now keep their pace.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: making unreported node load explicit.
Description check ✅ Passed The description directly explains the node load telemetry changes, compatibility behavior, API updates, and verification results.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/node-load-telemetry

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 6

🧹 Nitpick comments (4)
packages/sdk-python/tests/test_node.py (1)

336-339: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert the provenance flag in both SDK heartbeat tests.

Both tests verify that load is absent but do not prevent load_reported: true from appearing without a numeric load.

  • packages/sdk-python/tests/test_node.py#L336-L339: assert hb.get("load_reported") is not True.
  • packages/sdk-swift/Tests/RelaycastTests/NodeProviderTests.swift#L414-L421: assert heartbeat["load_reported"] is not .bool(true).
🤖 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 `@packages/sdk-python/tests/test_node.py` around lines 336 - 339, Update the
heartbeat assertions in packages/sdk-python/tests/test_node.py lines 336-339 to
verify hb.get("load_reported") is not True, and update
packages/sdk-swift/Tests/RelaycastTests/NodeProviderTests.swift lines 414-421 to
verify heartbeat["load_reported"] is not .bool(true); preserve the existing
assertions that load is absent.
packages/types/src/__tests__/fleet-wire-fixtures.test.ts (1)

123-144: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Add a regression case for numeric legacy loads.

The schema promises that numeric legacy load values remain parseable when load_reported is absent. This test covers omitted and null values, but not that compatibility path.

Proposed regression case
+    expect(parseFleetBrokerToRelaycastMessage({ ...heartbeat, load: 0 })).toEqual({
+      ...heartbeat,
+      load: 0,
+    });
🤖 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 `@packages/types/src/__tests__/fleet-wire-fixtures.test.ts` around lines 123 -
144, Extend the test “accepts unreported heartbeat load and rejects values
outside [0,1]” to assert that a numeric legacy load parses successfully when
load_reported is omitted. Use a valid numeric value and verify the parsed result
preserves the original heartbeat fields and load value.
packages/sdk-typescript/src/__tests__/node-provider.test.ts (1)

218-219: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert that the provider heartbeat is not marked as measured.

The test verifies that load is absent, but it does not verify the new provenance field. Add an assertion that load_reported is not true.

Proposed test assertion
 expect(hb).not.toHaveProperty('load');
+expect(hb).not.toHaveProperty('load_reported', true);
🤖 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 `@packages/sdk-typescript/src/__tests__/node-provider.test.ts` around lines 218
- 219, Update the heartbeat assertions in the node-provider test to verify that
the provider heartbeat’s load provenance field, load_reported, is not true,
while retaining the existing assertions for provider, active_agents,
handlers_live, and the absence of load.
packages/engine/src/engine/placement.ts (1)

93-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add deterministic placement-order coverage.

claimSpawnNode and chooseNodeForAction both sort candidates with compareNodeCapacityLoad. Add tests that assert the selection priority for reported versus unreported load and the activeAgents tie-break for unreported nodes.

🤖 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 `@packages/engine/src/engine/placement.ts` around lines 93 - 100, Add
deterministic tests covering candidate selection in claimSpawnNode and
chooseNodeForAction, both of which use compareNodeCapacityLoad. Assert
reported-load candidates take priority over unreported candidates, and
unreported candidates with equal load are ordered by activeAgents, preserving
deterministic selection.
🤖 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 `@CHANGELOG.md`:
- Around line 21-24: Update the Fleet node roster changelog entry to state that
broker-node load is reported only when every constituent provider reports a
genuine normalized capacity-utilization measurement; otherwise it remains null.
Match the direct-node and broker terminology already used in README.md and
openapi.yaml, including the max_agents: 0 unlimited behavior.
- Around line 21-24: Update the changelog’s unreleased section to use the
appropriate SemVer release-level heading format ([Unreleased - Patch],
[Unreleased - Minor], or [Unreleased - Major]). Split the existing fleet roster
entry into two short, impact-first bullets: one describing unavailable load as
null until explicitly reported, and another describing max_agents: 0 as
unlimited.

In `@openapi.yaml`:
- Around line 739-743: Update the max_agents property in the heartbeat response
schema to include a minimum of 0, while retaining its integer type and existing
description so negative values are rejected and zero remains the unlimited
sentinel.

In `@packages/engine/CHANGELOG.md`:
- Around line 14-15: Split the changelog entry into separate short, impact-first
bullets covering load provenance, migration 0034’s historical placeholder
behavior, GET /v1/nodes nullability, future-dated heartbeat freshness, and
unbounded aggregate capacity. Keep migration 0034 only with the historical-data
bullet, and omit internal or test-only details.
- Around line 12-15: Update the preceding changelog heading in
packages/engine/CHANGELOG.md at lines 12-15 and packages/sdk-swift/CHANGELOG.md
at lines 9-10 from [Unreleased] to the appropriate explicit SemVer release
level: [Unreleased - Patch], [Unreleased - Minor], or [Unreleased - Major].

In `@packages/sdk-rust/CHANGELOG.md`:
- Around line 9-14: Update the unreleased headings to use the explicit major
SemVer level for these breaking changes: change packages/sdk-rust/CHANGELOG.md
lines 9-14, packages/sdk-typescript/CHANGELOG.md lines 10-15, and
packages/types/CHANGELOG.md lines 10-15 to [Unreleased - Major].

---

Nitpick comments:
In `@packages/engine/src/engine/placement.ts`:
- Around line 93-100: Add deterministic tests covering candidate selection in
claimSpawnNode and chooseNodeForAction, both of which use
compareNodeCapacityLoad. Assert reported-load candidates take priority over
unreported candidates, and unreported candidates with equal load are ordered by
activeAgents, preserving deterministic selection.

In `@packages/sdk-python/tests/test_node.py`:
- Around line 336-339: Update the heartbeat assertions in
packages/sdk-python/tests/test_node.py lines 336-339 to verify
hb.get("load_reported") is not True, and update
packages/sdk-swift/Tests/RelaycastTests/NodeProviderTests.swift lines 414-421 to
verify heartbeat["load_reported"] is not .bool(true); preserve the existing
assertions that load is absent.

In `@packages/sdk-typescript/src/__tests__/node-provider.test.ts`:
- Around line 218-219: Update the heartbeat assertions in the node-provider test
to verify that the provider heartbeat’s load provenance field, load_reported, is
not true, while retaining the existing assertions for provider, active_agents,
handlers_live, and the absence of load.

In `@packages/types/src/__tests__/fleet-wire-fixtures.test.ts`:
- Around line 123-144: Extend the test “accepts unreported heartbeat load and
rejects values outside [0,1]” to assert that a numeric legacy load parses
successfully when load_reported is omitted. Use a valid numeric value and verify
the parsed result preserves the original heartbeat fields and load value.
🪄 Autofix

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: b1ee25f1-b88e-43af-833d-650f0b03ffb2

📥 Commits

Reviewing files that changed from the base of the PR and between 45beff3 and e08a7c5.

📒 Files selected for processing (34)
  • .agentworkforce/trajectories/completed/2026-08/traj_ae0vmeog5qmo/summary.md
  • .agentworkforce/trajectories/completed/2026-08/traj_ae0vmeog5qmo/trajectory.json
  • CHANGELOG.md
  • README.md
  • openapi.yaml
  • packages/engine/CHANGELOG.md
  • packages/engine/src/__tests__/conformance/node.test.ts
  • packages/engine/src/__tests__/conformance/nodeLiveness.test.ts
  • packages/engine/src/__tests__/conformance/nodeProviders.test.ts
  • packages/engine/src/db/migrations/0034_node_load_reporting.sql
  • packages/engine/src/db/schema.ts
  • packages/engine/src/engine/node.ts
  • packages/engine/src/engine/nodeProvider.ts
  • packages/engine/src/engine/placement.ts
  • packages/sdk-python/src/relay_sdk/models.py
  • packages/sdk-python/src/relay_sdk/node.py
  • packages/sdk-python/src/relay_sdk/ws.py
  • packages/sdk-python/tests/test_node.py
  • packages/sdk-rust/CHANGELOG.md
  • packages/sdk-rust/src/types.rs
  • packages/sdk-rust/src/ws.rs
  • packages/sdk-swift/CHANGELOG.md
  • packages/sdk-swift/Sources/Relaycast/Models.swift
  • packages/sdk-swift/Sources/Relaycast/NodeProvider.swift
  • packages/sdk-swift/Sources/Relaycast/WsClient.swift
  • packages/sdk-swift/Tests/RelaycastTests/NodeProviderTests.swift
  • packages/sdk-typescript/CHANGELOG.md
  • packages/sdk-typescript/src/__tests__/node-provider.test.ts
  • packages/sdk-typescript/src/node-provider.ts
  • packages/sdk-typescript/src/types.ts
  • packages/sdk-typescript/src/ws.ts
  • packages/types/CHANGELOG.md
  • packages/types/src/__tests__/fleet-wire-fixtures.test.ts
  • packages/types/src/fleet-wire.ts
💤 Files with no reviewable changes (2)
  • packages/sdk-swift/Sources/Relaycast/NodeProvider.swift
  • packages/sdk-typescript/src/node-provider.ts

Comment thread CHANGELOG.md
Comment thread openapi.yaml
Comment thread packages/engine/CHANGELOG.md
Comment thread packages/engine/CHANGELOG.md Outdated
Comment thread packages/sdk-rust/CHANGELOG.md Outdated

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 34 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/engine/src/db/migrations/0034_node_load_reporting.sql
Comment thread packages/sdk-python/src/relay_sdk/ws.py Outdated
Comment thread packages/sdk-rust/src/ws.rs Outdated
Comment thread packages/sdk-swift/CHANGELOG.md Outdated
Comment thread packages/sdk-typescript/CHANGELOG.md Outdated
Comment thread CHANGELOG.md Outdated
Comment thread packages/engine/src/__tests__/conformance/node.test.ts
Comment thread packages/sdk-typescript/src/ws.ts Outdated
Comment thread CHANGELOG.md Outdated
Comment thread openapi.yaml

@coderabbitai coderabbitai 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.

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 `@packages/sdk-rust/src/ws.rs`:
- Around line 759-760: Update the heartbeat assertion in the websocket test to
verify that the load field exists and equals JSON null, using
heartbeat.get("load") rather than indexing heartbeat["load"]; retain the
existing load_reported absence assertion.
🪄 Autofix

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: 691b2c38-97af-4fa4-b293-aa2f1ba37237

📥 Commits

Reviewing files that changed from the base of the PR and between e08a7c5 and 1a215d1.

📒 Files selected for processing (15)
  • CHANGELOG.md
  • openapi.yaml
  • packages/engine/CHANGELOG.md
  • packages/engine/src/__tests__/conformance/node.test.ts
  • packages/engine/src/db/migrations/0034_node_load_reporting.sql
  • packages/sdk-python/src/relay_sdk/ws.py
  • packages/sdk-python/tests/test_ws.py
  • packages/sdk-rust/CHANGELOG.md
  • packages/sdk-rust/src/ws.rs
  • packages/sdk-swift/CHANGELOG.md
  • packages/sdk-swift/Sources/Relaycast/WsClient.swift
  • packages/sdk-typescript/CHANGELOG.md
  • packages/sdk-typescript/src/__tests__/agent-ws.test.ts
  • packages/sdk-typescript/src/ws.ts
  • packages/types/CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/sdk-typescript/CHANGELOG.md
  • openapi.yaml
  • packages/engine/CHANGELOG.md
  • packages/types/CHANGELOG.md
  • packages/sdk-rust/CHANGELOG.md
  • packages/sdk-swift/CHANGELOG.md
  • CHANGELOG.md
  • packages/engine/src/db/migrations/0034_node_load_reporting.sql
  • packages/engine/src/tests/conformance/node.test.ts

Comment thread packages/sdk-rust/src/ws.rs Outdated
@khaliqgant
khaliqgant merged commit 41bb8bc into main Aug 7, 2026
5 checks passed
@khaliqgant
khaliqgant deleted the fix/node-load-telemetry branch August 7, 2026 18:19
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.

3 participants