Skip to content

fix(cli): unblock Publish Package — mock @agent-relay/sdk boundary in MCP startup test#1135

Merged
willwashburn merged 1 commit into
mainfrom
fix/publish-test-relaycast-sdk-duplication
Jun 16, 2026
Merged

fix(cli): unblock Publish Package — mock @agent-relay/sdk boundary in MCP startup test#1135
willwashburn merged 1 commit into
mainfrom
fix/publish-test-relaycast-sdk-duplication

Conversation

@willwashburn

@willwashburn willwashburn commented Jun 16, 2026

Copy link
Copy Markdown
Member

Problem

The Publish Package workflow (run 27598076243) fails its Run tests step with a single failure:

RelayError: Route not found
 ❯ relayErrorFromApi packages/sdk/node_modules/@relaycast/sdk/src/errors.ts:101:9
 ❯ Object.list           packages/sdk/src/messaging/relaycast.ts:984:8
 ❯ agent-relay-mcp.ts:1237  (query_nodes handler)
 ❯ agent-relay-mcp.startup.test.ts:352

This is release-blocking: npm test runs in the publish job, so the failure aborts the release.

Root cause

The query_nodes MCP tool constructs new AgentRelay(...) from @agent-relay/sdk and calls .nodes.list(). AgentRelay wraps @relaycast/sdk internally, but the test only mocks @relaycast/sdk — so the path works only when packages/cli and packages/sdk resolve the same physical @relaycast/sdk copy.

  • On main, @relaycast/sdk@4.0.0 hoists to a single root copy → the mock applies → green.
  • The publish job regenerates the lockfile (rm -rf package-lock.json && npm install) at the freshly-bumped version. The tree's pre-existing @relaycast/sdk skew (a stray transitive 1.2.0, plus @relayflows/core pinning ^1.1.0) tips the resolver into nesting a duplicate @relaycast/sdk under packages/sdk. AgentRelay's internal client is then the real, unmocked copy → live HTTP request → Route not found.

The stack confirms it: the unmocked client resolves from packages/sdk/node_modules/@relaycast/sdk, not the hoisted root copy.

This is a monorepo publish-time artifact, not a production bug — a real end-user install dedupes @relaycast/sdk@^4.0.0 to one copy, so AgentRelay works fine. Production code is therefore untouched.

Fix

Mock the direct boundary the handler uses (@agent-relay/sdk's AgentRelay) instead of the transitive @relaycast/sdk, via importActual + override so the real token-helper exports stay intact. The query_nodes path is now independent of node_modules hoisting — aligning with the repo convention to "mock the boundary, not internal modules".

Verification

Forced the nested-duplicate scenario locally (cp the root @relaycast/sdk into packages/sdk/node_modules):

nested duplicate present
pre-fix ❌ reproduces RelayError: Route not found
post-fix ✅ 16/16 pass
  • Full packages/cli suite: 362 passed, 5 skipped.
  • No production code changed.

Follow-up (not in this PR)

The underlying dep skew (@relaycast/sdk@1.2.0 / @relayflows/core@^1.1.0) is the same version-hygiene fragility tracked in #1134. This PR makes the test resilient; tightening the dep tree is separate.

🤖 Generated with Claude Code

Review in cubic

@willwashburn willwashburn requested a review from khaliqgant as a code owner June 16, 2026 06:38
@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 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The test file for the MCP startup flow adds a dedicated agentRelayNodesList mock function and an AgentRelayMock class wiring it to nodes.list. The SDK boundary mock is shifted from @relaycast/sdk to @agent-relay/sdk via vi.doMock, spreading the real module exports and substituting only AgentRelay.

Changes

CLI startup test: deterministic AgentRelay mock

Layer / File(s) Summary
AgentRelayMock and SDK boundary re-mock
packages/cli/src/cli/agent-relay-mcp.startup.test.ts
Adds agentRelayNodesList vi mock and AgentRelayMock returning { nodes: { list: agentRelayNodesList } }, plus a vi.doMock('@agent-relay/sdk', ...) that spreads actual exports and overrides AgentRelay with AgentRelayMock, replacing the prior @relaycast/sdk mock boundary.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

Possibly related issues

Possibly related PRs

  • AgentWorkforce/relay#1133: Directly related — the @relaycast/sdk version/override change in that PR triggered the duplicate-install resolution issue that this test mock fix addresses.

Suggested reviewers

  • khaliqgant

Poem

🐇 A mock was split in two one day,
The SDK boundary shifted away.
agentRelayNodesList returns with cheer,
No nested deps to meddle here.
The nodes list cleanly, test runs green — hooray! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% 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 accurately describes the main change: mocking the @agent-relay/sdk boundary in the MCP startup test to fix a release-blocking test failure.
Description check ✅ Passed The description comprehensively addresses the problem, root cause, fix, and verification; however, it does not follow the repository's pull request template structure (Summary, Test Plan, Screenshots).
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/publish-test-relaycast-sdk-duplication

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.

@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: 4

🤖 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
`@tests/integration/broker/evals-reports/report-2026-06-06T16-04-38-821Z-claude.json`:
- Around line 21-38: The scenario object in the fixture is missing required
fields that are mandatory according to tests/integration/broker/evals/types.ts
and are dereferenced by the HTML renderer. Add the required agents and
transcript fields to the scenario object (within the "01-dm-roundtrip" scenario
that starts at line 21) to ensure the fixture can round-trip through the eval
pipeline and be properly rendered by the HTML renderer.

In
`@tests/integration/broker/evals-reports/report-2026-06-06T22-01-51-691Z-claude.json`:
- Around line 98-110: The totalIntents field is currently set to 1, but the
notes field confirms both ACK and DONE intents were observed in the transcript,
indicating there are actually 2 distinct protocol intents. Update the
totalIntents value from 1 to 2 to accurately reflect the number of distinct
protocol intents present in this test fixture and ensure the report metrics are
correctly calculated.

In `@web/content/docs/fleets.mdx`:
- Around line 153-158: The shell command example contains inline comments placed
after line continuation backslashes on lines 154, 155, and 156, which breaks
command continuation when pasted. Remove the inline comments that appear after
the backslash characters on those lines (the comments about "override the node
name from the definition", "pass the workspace key inline instead of via env",
and "cap concurrently managed agents on this node"). The backslash must be the
last character on continuation lines with no text or comments following it.
Consider placing these explanatory comments on separate lines above the command
block or in a separate documentation section instead.

In `@web/next-env.d.ts`:
- Line 3: The import statement in next-env.d.ts is using the dev-specific path
`./.next/dev/types/routes.d.ts` which is not guaranteed to exist in all
environments (build, CI). Replace the dev-specific import path with the stable
Next.js 15 designated path `./.next/types/routes.d.ts`. Since this file is
auto-generated, try regenerating your build output first, which should correct
the path automatically. If the file is still not updated after regeneration,
manually edit the import to use the stable path `./.next/types/routes.d.ts`
instead.
🪄 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: e6b7dbd3-3646-4db3-b743-f9ffee87f7d0

📥 Commits

Reviewing files that changed from the base of the PR and between 893a37f and 4448a11.

📒 Files selected for processing (17)
  • packages/cli/src/cli/agent-relay-mcp.startup.test.ts
  • tests/integration/broker/evals-reports/report-2026-06-06T16-04-38-821Z-claude.json
  • tests/integration/broker/evals-reports/report-2026-06-06T21-57-43-350Z-claude.html
  • tests/integration/broker/evals-reports/report-2026-06-06T21-57-43-350Z-claude.json
  • tests/integration/broker/evals-reports/report-2026-06-06T22-01-51-691Z-claude.html
  • tests/integration/broker/evals-reports/report-2026-06-06T22-01-51-691Z-claude.json
  • tests/integration/broker/evals-reports/report-2026-06-07T02-39-03-749Z-claude.html
  • tests/integration/broker/evals-reports/report-2026-06-07T02-39-03-749Z-claude.json
  • web/app/docs/[slug]/page.tsx
  • web/components/docs/Mermaid.tsx
  • web/components/docs/docs.module.css
  • web/content/docs/fleets.mdx
  • web/lib/docs-nav.ts
  • web/lib/docs-versions.ts
  • web/lib/test/docs-versions.test.ts
  • web/next-env.d.ts
  • web/package.json
💤 Files with no reviewable changes (1)
  • web/lib/test/docs-versions.test.ts

Comment on lines +21 to +38
"scenarios": [
{
"id": "01-dm-roundtrip",
"title": "DM round-trip (A → B)",
"pass": true,
"sent": 1,
"expected": 1,
"phantoms": [],
"totalIntents": 0,
"protocolAdherence": null,
"wrongChannelReplies": 0,
"deliveryOk": true,
"events": {
"relayInbound": 2,
"dropped": 0,
"aclDenied": 0
}
}

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

Restore the required scenario payload.

agents and transcript are mandatory in tests/integration/broker/evals/types.ts, and the HTML renderer dereferences both. As written, this fixture will not round-trip through the eval pipeline.

🤖 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
`@tests/integration/broker/evals-reports/report-2026-06-06T16-04-38-821Z-claude.json`
around lines 21 - 38, The scenario object in the fixture is missing required
fields that are mandatory according to tests/integration/broker/evals/types.ts
and are dereferenced by the HTML renderer. Add the required agents and
transcript fields to the scenario object (within the "01-dm-roundtrip" scenario
that starts at line 21) to ensure the fixture can round-trip through the eval
pipeline and be properly rendered by the HTML renderer.

Comment on lines +98 to +110
"sent": 2,
"expected": 2,
"phantoms": [],
"totalIntents": 1,
"protocolAdherence": 1,
"wrongChannelReplies": 0,
"deliveryOk": true,
"events": {
"relayInbound": 3,
"dropped": 0,
"aclDenied": 0
},
"notes": "ack=true done=true order=true"

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 | 🟡 Minor | ⚡ Quick win

Correct the ACK/DONE intent count.

totalIntents is 1, but this transcript contains two distinct protocol intents (ACK and DONE), and the notes field confirms both were observed. That undercounts the scenario and skews the report metrics derived from this fixture.

🤖 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
`@tests/integration/broker/evals-reports/report-2026-06-06T22-01-51-691Z-claude.json`
around lines 98 - 110, The totalIntents field is currently set to 1, but the
notes field confirms both ACK and DONE intents were observed in the transcript,
indicating there are actually 2 distinct protocol intents. Update the
totalIntents value from 1 to 2 to accurately reflect the number of distinct
protocol intents present in this test fixture and ensure the report metrics are
correctly calculated.

Comment thread web/content/docs/fleets.mdx Outdated
Comment thread web/next-env.d.ts Outdated
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import './.next/types/routes.d.ts';
import './.next/dev/types/routes.d.ts';

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

🧩 Analysis chain

🌐 Web query:

In Next.js 15, what is the supported/expected path for generated typed-routes declarations referenced by next-env.d.ts, and is './.next/dev/types/routes.d.ts' valid for non-dev (build/CI) workflows?

💡 Result:

In Next.js 15, the supported and expected path for typed-routes declarations referenced in next-env.d.ts is ./.next/types/routes.d.ts [1][2]. This file serves as a stable entry point or proxy [3][4]. Under the hood, Next.js manages its internal structure to handle differences between development and production (build/CI) environments [3][5]. Specifically: 1. Stable Path: next-env.d.ts is designed to reference the stable path ./.next/types/routes.d.ts regardless of the current mode [3][4]. 2. Proxy Mechanism: When running in development (especially with features like isolatedDevBuild), Next.js may write a proxy file at .next/types/routes.d.ts that re-exports types from a deeper location (such as ./.next/dev/types/routes.d.ts) [3][4]. 3. Build/CI Workflows: In non-dev environments (e.g., next build or next typegen), the system generates the necessary definitions such that they are reachable via the stable path [6][7]. Is './.next/dev/types/routes.d.ts' valid for non-dev (build/CI) workflows? No, it is not considered the valid or stable path for build/CI workflows. Referencing ./.next/dev/types/routes.d.ts directly is an implementation detail of the development environment [3][4]. Hardcoding this path can lead to issues because: - The /dev/ directory is not guaranteed to exist or contain the required declarations during a standard production build [3][5]. - Next.js expects you to rely on the generated next-env.d.ts and the stable .next/types//*.ts glob pattern in your tsconfig.json to handle resolution automatically [6][8]. You should not manually edit next-env.d.ts to point to development-specific paths [6][7]. If you encounter a situation where next-env.d.ts is explicitly importing from a /dev/ path (e.g., during specific CLI commands), it is typically a sign that the generated file is reflecting a local state rather than the canonical production configuration [9]. Ensure your tsconfig.json includes .next/types//*.ts to allow the TypeScript compiler to resolve these routes correctly in all environments [6][10].

Citations:


🏁 Script executed:

# Check the actual content of the file
cat -n web/next-env.d.ts

# Also check if tsconfig.json exists and its configuration
find . -maxdepth 2 -name 'tsconfig.json' -type f | head -5

Repository: AgentWorkforce/relay

Length of output: 393


🏁 Script executed:

# Check the tsconfig.json files to see if proper includes are set
cat -n ./web/tsconfig.json

echo "---"

cat -n ./tsconfig.json

Repository: AgentWorkforce/relay

Length of output: 2594


Replace dev-specific import path with stable Next.js generated path.

Line 3 currently imports ./.next/dev/types/routes.d.ts, but Next.js 15 designates ./.next/types/routes.d.ts as the stable path for typed-routes declarations across all environments (dev, build, and CI). The /dev/ path is an implementation detail not guaranteed to exist during build workflows. Although this file is auto-generated, ensure your build process regenerates it with the correct stable path. If regeneration does not fix it, manually update the import to ./.next/types/routes.d.ts.

🤖 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 `@web/next-env.d.ts` at line 3, The import statement in next-env.d.ts is using
the dev-specific path `./.next/dev/types/routes.d.ts` which is not guaranteed to
exist in all environments (build, CI). Replace the dev-specific import path with
the stable Next.js 15 designated path `./.next/types/routes.d.ts`. Since this
file is auto-generated, try regenerating your build output first, which should
correct the path automatically. If the file is still not updated after
regeneration, manually edit the import to use the stable path
`./.next/types/routes.d.ts` instead.

@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: b96b529df7

ℹ️ 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 web/content/docs/fleets.mdx Outdated
Comment on lines +154 to +156
--name builder-1 \ # override the node name from the definition
--workspace rk_live_... \ # pass the workspace key inline instead of via env
--max-agents 8 \ # cap concurrently managed agents on this node

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 Move comments off continued shell lines

When this example is copied into a shell, the continuation backslashes are followed by spaces and comments instead of being the final character on the line, so Bash escapes a space and ends the command there; the next lines then run as separate --workspace/--max-agents commands. This makes the documented multi-line fleet serve override example fail for users unless the comments are moved to separate lines or the backslashes are placed immediately before the newline.

Useful? React with 👍 / 👎.

The query_nodes MCP tool constructs `new AgentRelay(...)` from
@agent-relay/sdk and calls `.nodes.list()`. AgentRelay wraps @relaycast/sdk
internally, so the test mocking only @relaycast/sdk left this path dependent
on packages/cli and packages/sdk resolving the SAME physical @relaycast/sdk
copy.

On main that copy hoists singly and the mock applies. But the Publish
Package job regenerates the lockfile (`rm -rf package-lock.json && npm
install`) at the freshly-bumped version, and the tree's pre-existing
@relaycast/sdk skew (a stray transitive 1.2.0, plus @relayflows/core
pinning ^1.1.0) tips the resolver into nesting a duplicate @relaycast/sdk
under packages/sdk. AgentRelay's internal client is then the real (unmocked)
one and `nodes.list()` escapes to a live HTTP request -> "RelayError: Route
not found", failing the release.

Mock the direct boundary (@agent-relay/sdk's AgentRelay) instead, so the
query_nodes path is independent of node_modules hoisting. Verified by forcing
the nested duplicate locally: pre-fix reproduces the exact failure, post-fix
passes (16/16). Production code is untouched; in a real install
@relaycast/sdk@^4.0.0 dedupes to one copy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@willwashburn willwashburn force-pushed the fix/publish-test-relaycast-sdk-duplication branch from 4448a11 to b99a26e Compare June 16, 2026 06:52
@willwashburn

Copy link
Copy Markdown
Member Author

Cleaned up and rebased onto current main. The branch was based on a stale main and a force-add had swept in a batch of unrelated working-tree files (the evals-reports/* fixtures, web/content/docs/fleets.mdx, Mermaid.tsx, docs-versions.ts, next-env.d.ts, etc.), which is what created both the merge conflicts and all the CodeRabbit/Codex review comments.

The PR now contains only the intended one-file change — the @agent-relay/sdk mock in agent-relay-mcp.startup.test.ts — and is mergeable. All prior inline review comments were on those swept-in files and no longer apply here.

Note for reviewers: none of the bot findings touched the actual fix. The legitimate fleets.mdx finding (inline comments after \ line-continuations) belongs to the docs work in #1136 and is being handled there.

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

🧹 Nitpick comments (1)
packages/cli/src/cli/agent-relay-mcp.startup.test.ts (1)

210-221: ⚡ Quick win

Assert forwarded filters on the new AgentRelay boundary mock.

agentRelayNodesList is now the right seam, but the test currently only checks output shape. If query_nodes stops forwarding capability/name, the static mock result still passes. Please assert the call arguments (e.g., on Line 377 path) to lock the contract.

Suggested diff
@@
   return {
     mod,
     mocks: {
       behavior,
       serverInstances,
       relayInstances,
+      agentRelayNodesList,
       telemetryTrack,
       telemetryInit,
       telemetryShutdown,
       RelayCast,
       FakeTransport,
@@
     const queryNodesResult = await server.tools.get('query_nodes')?.handler({ capability: 'spawn:codex' });
+    expect(mocks.agentRelayNodesList).toHaveBeenCalledWith({
+      capability: 'spawn:codex',
+      name: undefined,
+    });
     expect(queryNodesResult.structuredContent.nodes).toEqual([

As per coding guidelines, “Trace context ... cross-reference related files,” this recommendation is based on the query_nodes handler contract in packages/cli/src/cli/agent-relay-mcp.ts (relay.nodes.list({ capability, name })).

🤖 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/cli/src/cli/agent-relay-mcp.startup.test.ts` around lines 210 - 221,
The test mock for agentRelayNodesList only validates the output shape but does
not assert that the correct filter arguments (capability and name) are being
forwarded to it. Add an assertion on the agentRelayNodesList mock to verify it
was called with the expected arguments that match the contract defined in
relay.nodes.list({ capability, name }). This will ensure that if query_nodes
stops forwarding these filters, the test will fail and catch the regression.
🤖 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.

Nitpick comments:
In `@packages/cli/src/cli/agent-relay-mcp.startup.test.ts`:
- Around line 210-221: The test mock for agentRelayNodesList only validates the
output shape but does not assert that the correct filter arguments (capability
and name) are being forwarded to it. Add an assertion on the agentRelayNodesList
mock to verify it was called with the expected arguments that match the contract
defined in relay.nodes.list({ capability, name }). This will ensure that if
query_nodes stops forwarding these filters, the test will fail and catch the
regression.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7ffdd2e9-2813-4e59-8093-4b00e5c37ab1

📥 Commits

Reviewing files that changed from the base of the PR and between 4448a11 and b99a26e.

📒 Files selected for processing (1)
  • packages/cli/src/cli/agent-relay-mcp.startup.test.ts

@willwashburn willwashburn merged commit e2fc988 into main Jun 16, 2026
46 checks passed
@willwashburn willwashburn deleted the fix/publish-test-relaycast-sdk-duplication branch June 16, 2026 07:02
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