Skip to content

test(e2e): Add a node-flue end-to-end application - #24377

Merged
RulaKhaled merged 5 commits into
developfrom
feat/flue-e2e-app
Sep 17, 2026
Merged

RulaKhaled merged 5 commits into
developfrom
feat/flue-e2e-app

Conversation

@RulaKhaled

@RulaKhaled RulaKhaled commented Sep 14, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #24266 — review that first, this is the e2e app only.

The node-integration-test calls __flueBindAgentModule by hand, standing in for what @flue/vite does from the 'use agent' directive, so it can't show whether a scaffolded app actually works. This is flue init output, built and served the way a user runs it, against a real provider.

Covered, in both dev and prod and in both the base and orchestrion variants:

  • invoke_agent / chat / execute_tool with usage and cost
  • a thrown tool error becoming an issue
  • a manual span inside a tool nesting under execute_tool
  • an orchestrion-instrumented dataloader span landing in the agent's trace
  • the provider's http.client call nesting inside chat

The loader runs inside a tool rather than a route so its span shares the agent's trace. No build externals are needed unlike node-eve — a Flue node build leaves dependencies as bare specifiers, so dataloader stays a real module for the transform to hook. @flue/* is pinned because the internal registry proxy 403s on releases it hasn't scanned.

Found: a thrown tool error produced an errored span and no issue at all, since Flue catches the throw and hands it back to the model as a tool result. Fixed in #24265.

@RulaKhaled
RulaKhaled added this pull request to stack #24376 September 14, 2026 14:05
@github-actions

github-actions Bot commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 29.09 kB - -
@sentry/browser - with treeshaking flags 27.35 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.26 kB - -
@sentry/browser (incl. Tracing) 50.6 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 50.62 kB - -
@sentry/browser (incl. Tracing, Profiling) 53.61 kB - -
@sentry/browser (incl. Tracing, Replay) 90.15 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 79.25 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 94.85 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 107.83 kB - -
@sentry/browser (incl. Feedback) 46.62 kB - -
@sentry/browser (incl. sendFeedback) 34.15 kB - -
@sentry/browser (incl. FeedbackAsync) 39.26 kB - -
@sentry/browser (incl. Metrics) 30.1 kB - -
@sentry/browser (incl. Logs) 30.35 kB - -
@sentry/browser (incl. Metrics & Logs) 31.02 kB - -
@sentry/react 30.84 kB - -
@sentry/react (incl. Tracing) 52.94 kB - -
@sentry/vue 36.34 kB - -
@sentry/vue (incl. Tracing) 52.91 kB - -
@sentry/svelte 29.11 kB - -
CDN Bundle 30.8 kB - -
CDN Bundle (incl. Tracing) 51.15 kB - -
CDN Bundle (incl. Logs, Metrics) 33.06 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 53.14 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 73.75 kB - -
CDN Bundle (incl. Tracing, Replay) 88.69 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 90.63 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 94.73 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 96.78 kB - -
CDN Bundle - uncompressed 91.16 kB - -
CDN Bundle (incl. Tracing) - uncompressed 152.66 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.73 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 158.61 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 227.14 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 272.23 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 278.17 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 285.93 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 291.86 kB - -
@sentry/nextjs (client) 55.27 kB - -
@sentry/sveltekit (client) 51.05 kB - -
@sentry/core/server 39.63 kB - -
@sentry/core/browser 13.66 kB - -
@sentry/node 132.37 kB +0.02% +18 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 82.03 kB - -
@sentry/node - without tracing 89.82 kB +0.04% +30 B 🔺
@sentry/node - without channel injection 111.23 kB +0.02% +18 B 🔺
@sentry/aws-serverless 98.06 kB +0.03% +25 B 🔺
@sentry/cloudflare (withSentry) - minified 204.52 kB - -
@sentry/cloudflare (withSentry) 508.73 kB - -

View base workflow run

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

Stale Bugbot comment from a previous run.

Comment thread dev-packages/e2e-tests/test-applications/node-flue/tests/utils.ts
Comment thread dev-packages/e2e-tests/test-applications/node-flue/src/agents/hello.ts Outdated
@RulaKhaled
RulaKhaled requested review from a team as code owners September 14, 2026 20:04
@RulaKhaled
RulaKhaled requested review from JPeer264 and mydea and removed request for a team September 14, 2026 20:04
Comment thread dev-packages/e2e-tests/test-applications/node-flue/sentry-init.ts Outdated
Comment thread dev-packages/e2e-tests/test-applications/node-flue/package.json Outdated
Comment thread dev-packages/e2e-tests/test-applications/node-flue/vite.config.ts Outdated
Comment thread dev-packages/e2e-tests/test-applications/node-flue/vite.config.ts Outdated
const hasOps = (ops: string[]) => (spansOfTrace: { attributes?: Record<string, { value?: unknown }> }[]) =>
ops.every(op => spansOfTrace.some(span => getSpanOp(span) === op));

test('captures the invoke_agent / chat / execute_tool hierarchy for a Flue turn', async ({ baseURL }) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

somewhere here we should also test the http.server spans. as this uses hono, it will today likely be unparametrized, which we should codify in the test. once #24371 lands this should automatically be captured as hono spans too then!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

had a go at this but couldn't get the assertion working, adding http.server to the collectStreamedSpans predicate hangs, same with waitForTransaction on the op. the segment ships in its own envelope at the 202, well before the children, so i suspect the proxy/helper path rather than the sdk, clanker REALLY went through it 😅 i couldn't pin it down yet

the spans themselves are fine though. dumped the transport for one turn: single trace, agent tree parented under http.server, and unparametrized exactly as you guessed POST

I'll leave the assertion out rather than landing something flaky, happy to revisit once #24371 lands since the shape changes there anyway

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

Stale Bugbot comment from a previous run.

Comment thread dev-packages/e2e-tests/test-applications/node-flue/tests/utils.ts
Comment thread dev-packages/e2e-tests/test-applications/node-flue/src/loaders.ts Outdated
@RulaKhaled
RulaKhaled requested a review from mydea September 15, 2026 11:47

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

Stale Bugbot comment from a previous run.

{ name: 'resolve-weather', attributes: { 'weather.source': 'static-table', 'weather.city': city } },
() => {
return `It is 21 degrees and sunny in ${city}.`;
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Tool handler reads wrong arguments

Medium Severity

get_weather's run callback destructures city from the tool context, but Flue puts validated input on data. city is therefore always undefined, so the model gets a useless tool result and the weather.city span attribute is never set.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8d0cf18. Configure here.

Base automatically changed from feat/flue-instrumentation-tests to develop September 17, 2026 11:25
The node-integration-test suite drives Flue from a hand-written scenario that
calls `__flueBindAgentModule` itself, standing in for what `@flue/vite` does at
build time. It cannot show whether a scaffolded app works. This app is what
`flue init` produces — plain vite, `'use agent'`, `createAgentRouter` — built and
served the way a user runs it, against a real provider.

Covers: AI spans, errors captured as issues, a manual span nesting inside a tool,
an orchestrion-instrumented `dataloader` span landing in the agent's trace, and
both dev and prod. Also asserts the provider's HTTP call nests inside `chat`,
which nothing else covers.

The loader is called from inside a tool rather than a route, so its span shares
the agent's trace instead of sitting in one of its own.

No build externals are needed, unlike node-eve: a Flue node build leaves
dependencies as bare specifiers, so `dataloader` stays a real module for the
transform to hook. The `@flue/*` versions are pinned because the internal
registry proxy 403s on releases it has not scanned, and a caret range drifts onto
them; the `(latest)` variant is where new versions get exercised.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RulaKhaled and others added 4 commits September 17, 2026 14:25
Two lines over the width limit; I formatted the server-utils sources but not the
test application.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`runAgentTurn` returned on the `202` and dropped the `streamUrl`, so a turn kept
running while the next test started waiting for spans — a leftover trace could
satisfy the wrong assertion. It now reads the conversation back until it reports
a settlement.

Scoping the waits by `gen_ai.conversation.id` would not have worked: Flue
generates that id (`conv_01M2G81…`), so it is not the path segment the test
chose and the test cannot know it up front.

Also names `count_items` in the agent instructions — an earlier edit missed, so
the dataloader test was relying on the tool description alone — and drops the
`loaders.ts` comment describing the route-based setup that no longer exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Drop the explicit `dataloaderIntegration()`; it is in `getTracingIntegrations()`
  now, so Node registers it by default when spans are enabled.
- Always run under orchestrion rather than keeping it as a variant, since that
  is how the SDK is meant to be set up. Removes the `*:orchestrion` scripts, the
  `USE_ORCHESTRION` plumbing and the `test.fail()` branch in the dataloader test,
  which now simply asserts the span lands under `execute_tool`.
- Drop the node-eve reference from `vite.config.ts`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three things made it flaky on a Playwright retry, which is why the dev run went
red then green:

- `DataLoader` was a module-level singleton, so it cached keys 1-3 and a second
  `count_items` call skipped the batch function and emitted no span. Constructed
  per execution now, matching node-eve.
- Conversation ids were fixed strings, so `runAgentTurn` saw a settlement from an
  earlier run and returned before the new turn finished. Each turn gets its own.
- The waits matched any agent turn, so a leftover trace could satisfy the wrong
  test. Each now anchors on its own tool via `gen_ai.tool.name`.

The dataloader test asserts a shared trace rather than the exact parent: the
model may call the tool more than once, and the span that ran the loader is not
reliably the one found by name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1478c0a. Configure here.

const providerCall = spans.find(span => getSpanOp(span) === 'http.client');

expect(providerCall?.trace_id).toBe(chat?.trace_id);
expect(providerCall?.parent_span_id).toBe(chat?.span_id);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

HTTP nesting test matches leftover traces

Medium Severity

This test’s collectStreamedSpans predicate only looks for gen_ai.chat and http.client, which every Flue turn emits. collectStreamedSpans resolves on the earliest matching trace, so a late envelope from the preceding weather tests can satisfy it. Flagged because the testing conventions require wait conditions unique enough that leftover telemetry cannot win.

Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Reviewed by Cursor Bugbot for commit 1478c0a. Configure here.

@RulaKhaled
RulaKhaled merged commit a6571ee into develop Sep 17, 2026
44 checks passed
@RulaKhaled
RulaKhaled deleted the feat/flue-e2e-app branch September 17, 2026 12:00
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