test(e2e): Port cloudflare-agent to span streaming - #23862

Merged
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent
Sep 2, 2026
Merged

test(e2e): Port cloudflare-agent to span streaming#23862
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent

Conversation

@JPeer264

@JPeer264JPeer264 commented Sep 1, 2026

Copy link
Copy Markdown
Member

Ports cloudflare-agent to span streaming. The gen_ai assertions were already on streamed spans; the remaining waitForTransaction calls are replaced.

  • The worker and the Agent Durable Object both emit a GET segment for the same URL. The DO segment is picked by url.path plus a defined parent_span_id (the worker propagates its trace over the RPC binding), same discriminator the old spec used.
  • The three surviving storage spans are children of the greet rpc span, so they are selected by parent_span_id and ordered by start_timestamp; the count-based filter proof is unchanged.
  • gen_ai-to-segment relations rely on collectStreamedSpans grouping by trace (test(e2e): Group collectStreamedSpans by trace #23912): the collect finishes once the owning segment has arrived, so no manual trace_id matching is needed.

@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.12 kB+0.02%+20 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+20 B 🔺
@sentry/node - without channel injection103.42 kB+0.02%+18 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from b150a0c to 110feb6CompareSeptember 1, 2026 15:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 110feb6 to 1af4495CompareSeptember 2, 2026 06:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1af4495 to 1f6e74bCompareSeptember 2, 2026 07:10
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1f6e74b to c237b43CompareSeptember 2, 2026 07:46
@JPeer264
JPeer264 marked this pull request as ready for review September 2, 2026 07:47
transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.description === 'durable_object_storage_put')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

The trace id condition is no longer needed if you rebase develop.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here too.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const rpcSpan = spans.find(span => getSpanOp(span) === 'rpc' && span.name === 'greet')!;

const rpcSpans = (transaction.spans ?? []).filter(span => span.op === 'rpc');
const rpcSpans = spans.filter(span => getSpanOp(span) === 'rpc' && span.trace_id === rpcSpan.trace_id);

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const genAiSpan = spans.find(span => getSpanOp(span) === 'gen_ai.chat');
return (
!!genAiSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === genAiSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from c237b43 to df60649CompareSeptember 2, 2026 11:30
Comment on lines +127 to +135
const spansPromise = collectStreamedSpans('cloudflare-agent', spans =>
spans.some(
span =>
getSpanOp(span) === 'http.server' &&
span.is_segment &&
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
span.parent_span_id !== undefined,
),
);

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.

Bug: The test for internal db.query spans is ineffective because it monitors a trace that is unlikely to contain these spans, causing the test to always pass.
Severity: LOW

Suggested Fix

The test should be redesigned to capture spans from multiple, potentially unrelated traces simultaneously. Instead of resolving collectStreamedSpans on a single trace's predicate, consider a mechanism that can observe all spans received by the event proxy over a period of time, regardless of their trace_id, and then perform the assertion.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/callable.test.ts#L127-L135
Potential issue: The test at `callable.test.ts` is intended to verify that internal
`db.query` spans from the agents runtime are not emitted. It uses `collectStreamedSpans`
to listen for spans on the `http.server` trace. However, the internal database queries
for bookkeeping tables like `cf_agents_*` likely execute in a separate trace context
from the main request. Because `collectStreamedSpans` groups spans by `trace_id`, it
will only capture spans from the `http.server` trace and will never see the internal
`db.query` spans from the other trace. This causes the test to pass trivially with zero
results, rendering it ineffective at detecting regressions where these internal spans
might start leaking.

Did we get this right? 👍 / 👎 to inform future reviews.

@JPeer264
JPeer264 merged commit 3a9667e into developSep 2, 2026
46 checks passed
@JPeer264
JPeer264 deleted the jp/e2e-stream-cloudflare-agent branch September 2, 2026 12:26
Sign up for freeto 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

@JPeer264@s1gr1d@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

test(e2e): Port cloudflare-agent to span streaming - #23862

Merged
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent
Sep 2, 2026
Merged

test(e2e): Port cloudflare-agent to span streaming#23862
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent

Conversation

@JPeer264

@JPeer264JPeer264 commented Sep 1, 2026

Copy link
Copy Markdown
Member

Ports cloudflare-agent to span streaming. The gen_ai assertions were already on streamed spans; the remaining waitForTransaction calls are replaced.

  • The worker and the Agent Durable Object both emit a GET segment for the same URL. The DO segment is picked by url.path plus a defined parent_span_id (the worker propagates its trace over the RPC binding), same discriminator the old spec used.
  • The three surviving storage spans are children of the greet rpc span, so they are selected by parent_span_id and ordered by start_timestamp; the count-based filter proof is unchanged.
  • gen_ai-to-segment relations rely on collectStreamedSpans grouping by trace (test(e2e): Group collectStreamedSpans by trace #23912): the collect finishes once the owning segment has arrived, so no manual trace_id matching is needed.

@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.12 kB+0.02%+20 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+20 B 🔺
@sentry/node - without channel injection103.42 kB+0.02%+18 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from b150a0c to 110feb6CompareSeptember 1, 2026 15:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 110feb6 to 1af4495CompareSeptember 2, 2026 06:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1af4495 to 1f6e74bCompareSeptember 2, 2026 07:10
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1f6e74b to c237b43CompareSeptember 2, 2026 07:46
@JPeer264
JPeer264 marked this pull request as ready for review September 2, 2026 07:47
transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.description === 'durable_object_storage_put')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

The trace id condition is no longer needed if you rebase develop.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here too.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const rpcSpan = spans.find(span => getSpanOp(span) === 'rpc' && span.name === 'greet')!;

const rpcSpans = (transaction.spans ?? []).filter(span => span.op === 'rpc');
const rpcSpans = spans.filter(span => getSpanOp(span) === 'rpc' && span.trace_id === rpcSpan.trace_id);

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const genAiSpan = spans.find(span => getSpanOp(span) === 'gen_ai.chat');
return (
!!genAiSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === genAiSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from c237b43 to df60649CompareSeptember 2, 2026 11:30
Comment on lines +127 to +135
const spansPromise = collectStreamedSpans('cloudflare-agent', spans =>
spans.some(
span =>
getSpanOp(span) === 'http.server' &&
span.is_segment &&
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
span.parent_span_id !== undefined,
),
);

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.

Bug: The test for internal db.query spans is ineffective because it monitors a trace that is unlikely to contain these spans, causing the test to always pass.
Severity: LOW

Suggested Fix

The test should be redesigned to capture spans from multiple, potentially unrelated traces simultaneously. Instead of resolving collectStreamedSpans on a single trace's predicate, consider a mechanism that can observe all spans received by the event proxy over a period of time, regardless of their trace_id, and then perform the assertion.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/callable.test.ts#L127-L135
Potential issue: The test at `callable.test.ts` is intended to verify that internal
`db.query` spans from the agents runtime are not emitted. It uses `collectStreamedSpans`
to listen for spans on the `http.server` trace. However, the internal database queries
for bookkeeping tables like `cf_agents_*` likely execute in a separate trace context
from the main request. Because `collectStreamedSpans` groups spans by `trace_id`, it
will only capture spans from the `http.server` trace and will never see the internal
`db.query` spans from the other trace. This causes the test to pass trivially with zero
results, rendering it ineffective at detecting regressions where these internal spans
might start leaking.

Did we get this right? 👍 / 👎 to inform future reviews.

@JPeer264
JPeer264 merged commit 3a9667e into developSep 2, 2026
46 checks passed
@JPeer264
JPeer264 deleted the jp/e2e-stream-cloudflare-agent branch September 2, 2026 12:26
Sign up for freeto 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

@JPeer264@s1gr1d@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): Port cloudflare-agent to span streaming - #23862

Merged
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent
Sep 2, 2026
Merged

test(e2e): Port cloudflare-agent to span streaming#23862
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent

Conversation

@JPeer264

@JPeer264JPeer264 commented Sep 1, 2026

Copy link
Copy Markdown
Member

Ports cloudflare-agent to span streaming. The gen_ai assertions were already on streamed spans; the remaining waitForTransaction calls are replaced.

  • The worker and the Agent Durable Object both emit a GET segment for the same URL. The DO segment is picked by url.path plus a defined parent_span_id (the worker propagates its trace over the RPC binding), same discriminator the old spec used.
  • The three surviving storage spans are children of the greet rpc span, so they are selected by parent_span_id and ordered by start_timestamp; the count-based filter proof is unchanged.
  • gen_ai-to-segment relations rely on collectStreamedSpans grouping by trace (test(e2e): Group collectStreamedSpans by trace #23912): the collect finishes once the owning segment has arrived, so no manual trace_id matching is needed.

@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.12 kB+0.02%+20 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+20 B 🔺
@sentry/node - without channel injection103.42 kB+0.02%+18 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from b150a0c to 110feb6CompareSeptember 1, 2026 15:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 110feb6 to 1af4495CompareSeptember 2, 2026 06:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1af4495 to 1f6e74bCompareSeptember 2, 2026 07:10
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1f6e74b to c237b43CompareSeptember 2, 2026 07:46
@JPeer264
JPeer264 marked this pull request as ready for review September 2, 2026 07:47
transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.description === 'durable_object_storage_put')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

The trace id condition is no longer needed if you rebase develop.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here too.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const rpcSpan = spans.find(span => getSpanOp(span) === 'rpc' && span.name === 'greet')!;

const rpcSpans = (transaction.spans ?? []).filter(span => span.op === 'rpc');
const rpcSpans = spans.filter(span => getSpanOp(span) === 'rpc' && span.trace_id === rpcSpan.trace_id);

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const genAiSpan = spans.find(span => getSpanOp(span) === 'gen_ai.chat');
return (
!!genAiSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === genAiSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from c237b43 to df60649CompareSeptember 2, 2026 11:30
Comment on lines +127 to +135
const spansPromise = collectStreamedSpans('cloudflare-agent', spans =>
spans.some(
span =>
getSpanOp(span) === 'http.server' &&
span.is_segment &&
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
span.parent_span_id !== undefined,
),
);

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.

Bug: The test for internal db.query spans is ineffective because it monitors a trace that is unlikely to contain these spans, causing the test to always pass.
Severity: LOW

Suggested Fix

The test should be redesigned to capture spans from multiple, potentially unrelated traces simultaneously. Instead of resolving collectStreamedSpans on a single trace's predicate, consider a mechanism that can observe all spans received by the event proxy over a period of time, regardless of their trace_id, and then perform the assertion.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/callable.test.ts#L127-L135
Potential issue: The test at `callable.test.ts` is intended to verify that internal
`db.query` spans from the agents runtime are not emitted. It uses `collectStreamedSpans`
to listen for spans on the `http.server` trace. However, the internal database queries
for bookkeeping tables like `cf_agents_*` likely execute in a separate trace context
from the main request. Because `collectStreamedSpans` groups spans by `trace_id`, it
will only capture spans from the `http.server` trace and will never see the internal
`db.query` spans from the other trace. This causes the test to pass trivially with zero
results, rendering it ineffective at detecting regressions where these internal spans
might start leaking.

Did we get this right? 👍 / 👎 to inform future reviews.

@JPeer264
JPeer264 merged commit 3a9667e into developSep 2, 2026
46 checks passed
@JPeer264
JPeer264 deleted the jp/e2e-stream-cloudflare-agent branch September 2, 2026 12:26
Sign up for freeto 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

@JPeer264@s1gr1d@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): Port cloudflare-agent to span streaming - #23862

Merged
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent
Sep 2, 2026
Merged

test(e2e): Port cloudflare-agent to span streaming#23862
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent

Conversation

@JPeer264

@JPeer264JPeer264 commented Sep 1, 2026

Copy link
Copy Markdown
Member

Ports cloudflare-agent to span streaming. The gen_ai assertions were already on streamed spans; the remaining waitForTransaction calls are replaced.

  • The worker and the Agent Durable Object both emit a GET segment for the same URL. The DO segment is picked by url.path plus a defined parent_span_id (the worker propagates its trace over the RPC binding), same discriminator the old spec used.
  • The three surviving storage spans are children of the greet rpc span, so they are selected by parent_span_id and ordered by start_timestamp; the count-based filter proof is unchanged.
  • gen_ai-to-segment relations rely on collectStreamedSpans grouping by trace (test(e2e): Group collectStreamedSpans by trace #23912): the collect finishes once the owning segment has arrived, so no manual trace_id matching is needed.

@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.12 kB+0.02%+20 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+20 B 🔺
@sentry/node - without channel injection103.42 kB+0.02%+18 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from b150a0c to 110feb6CompareSeptember 1, 2026 15:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 110feb6 to 1af4495CompareSeptember 2, 2026 06:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1af4495 to 1f6e74bCompareSeptember 2, 2026 07:10
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1f6e74b to c237b43CompareSeptember 2, 2026 07:46
@JPeer264
JPeer264 marked this pull request as ready for review September 2, 2026 07:47
transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.description === 'durable_object_storage_put')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

The trace id condition is no longer needed if you rebase develop.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here too.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const rpcSpan = spans.find(span => getSpanOp(span) === 'rpc' && span.name === 'greet')!;

const rpcSpans = (transaction.spans ?? []).filter(span => span.op === 'rpc');
const rpcSpans = spans.filter(span => getSpanOp(span) === 'rpc' && span.trace_id === rpcSpan.trace_id);

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const genAiSpan = spans.find(span => getSpanOp(span) === 'gen_ai.chat');
return (
!!genAiSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === genAiSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from c237b43 to df60649CompareSeptember 2, 2026 11:30
Comment on lines +127 to +135
const spansPromise = collectStreamedSpans('cloudflare-agent', spans =>
spans.some(
span =>
getSpanOp(span) === 'http.server' &&
span.is_segment &&
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
span.parent_span_id !== undefined,
),
);

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.

Bug: The test for internal db.query spans is ineffective because it monitors a trace that is unlikely to contain these spans, causing the test to always pass.
Severity: LOW

Suggested Fix

The test should be redesigned to capture spans from multiple, potentially unrelated traces simultaneously. Instead of resolving collectStreamedSpans on a single trace's predicate, consider a mechanism that can observe all spans received by the event proxy over a period of time, regardless of their trace_id, and then perform the assertion.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/callable.test.ts#L127-L135
Potential issue: The test at `callable.test.ts` is intended to verify that internal
`db.query` spans from the agents runtime are not emitted. It uses `collectStreamedSpans`
to listen for spans on the `http.server` trace. However, the internal database queries
for bookkeeping tables like `cf_agents_*` likely execute in a separate trace context
from the main request. Because `collectStreamedSpans` groups spans by `trace_id`, it
will only capture spans from the `http.server` trace and will never see the internal
`db.query` spans from the other trace. This causes the test to pass trivially with zero
results, rendering it ineffective at detecting regressions where these internal spans
might start leaking.

Did we get this right? 👍 / 👎 to inform future reviews.

@JPeer264
JPeer264 merged commit 3a9667e into developSep 2, 2026
46 checks passed
@JPeer264
JPeer264 deleted the jp/e2e-stream-cloudflare-agent branch September 2, 2026 12:26
Sign up for freeto 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

@JPeer264@s1gr1d@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

test(e2e): Port cloudflare-agent to span streaming - #23862

Merged
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent
Sep 2, 2026
Merged

test(e2e): Port cloudflare-agent to span streaming#23862
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent

Conversation

@JPeer264

@JPeer264JPeer264 commented Sep 1, 2026

Copy link
Copy Markdown
Member

Ports cloudflare-agent to span streaming. The gen_ai assertions were already on streamed spans; the remaining waitForTransaction calls are replaced.

  • The worker and the Agent Durable Object both emit a GET segment for the same URL. The DO segment is picked by url.path plus a defined parent_span_id (the worker propagates its trace over the RPC binding), same discriminator the old spec used.
  • The three surviving storage spans are children of the greet rpc span, so they are selected by parent_span_id and ordered by start_timestamp; the count-based filter proof is unchanged.
  • gen_ai-to-segment relations rely on collectStreamedSpans grouping by trace (test(e2e): Group collectStreamedSpans by trace #23912): the collect finishes once the owning segment has arrived, so no manual trace_id matching is needed.

@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.12 kB+0.02%+20 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+20 B 🔺
@sentry/node - without channel injection103.42 kB+0.02%+18 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from b150a0c to 110feb6CompareSeptember 1, 2026 15:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 110feb6 to 1af4495CompareSeptember 2, 2026 06:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1af4495 to 1f6e74bCompareSeptember 2, 2026 07:10
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1f6e74b to c237b43CompareSeptember 2, 2026 07:46
@JPeer264
JPeer264 marked this pull request as ready for review September 2, 2026 07:47
transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.description === 'durable_object_storage_put')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

The trace id condition is no longer needed if you rebase develop.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here too.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const rpcSpan = spans.find(span => getSpanOp(span) === 'rpc' && span.name === 'greet')!;

const rpcSpans = (transaction.spans ?? []).filter(span => span.op === 'rpc');
const rpcSpans = spans.filter(span => getSpanOp(span) === 'rpc' && span.trace_id === rpcSpan.trace_id);

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const genAiSpan = spans.find(span => getSpanOp(span) === 'gen_ai.chat');
return (
!!genAiSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === genAiSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from c237b43 to df60649CompareSeptember 2, 2026 11:30
Comment on lines +127 to +135
const spansPromise = collectStreamedSpans('cloudflare-agent', spans =>
spans.some(
span =>
getSpanOp(span) === 'http.server' &&
span.is_segment &&
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
span.parent_span_id !== undefined,
),
);

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.

Bug: The test for internal db.query spans is ineffective because it monitors a trace that is unlikely to contain these spans, causing the test to always pass.
Severity: LOW

Suggested Fix

The test should be redesigned to capture spans from multiple, potentially unrelated traces simultaneously. Instead of resolving collectStreamedSpans on a single trace's predicate, consider a mechanism that can observe all spans received by the event proxy over a period of time, regardless of their trace_id, and then perform the assertion.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/callable.test.ts#L127-L135
Potential issue: The test at `callable.test.ts` is intended to verify that internal
`db.query` spans from the agents runtime are not emitted. It uses `collectStreamedSpans`
to listen for spans on the `http.server` trace. However, the internal database queries
for bookkeeping tables like `cf_agents_*` likely execute in a separate trace context
from the main request. Because `collectStreamedSpans` groups spans by `trace_id`, it
will only capture spans from the `http.server` trace and will never see the internal
`db.query` spans from the other trace. This causes the test to pass trivially with zero
results, rendering it ineffective at detecting regressions where these internal spans
might start leaking.

Did we get this right? 👍 / 👎 to inform future reviews.

@JPeer264
JPeer264 merged commit 3a9667e into developSep 2, 2026
46 checks passed
@JPeer264
JPeer264 deleted the jp/e2e-stream-cloudflare-agent branch September 2, 2026 12:26
Sign up for freeto 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

@JPeer264@s1gr1d@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): Port cloudflare-agent to span streaming - #23862

Merged
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent
Sep 2, 2026
Merged

test(e2e): Port cloudflare-agent to span streaming#23862
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent

Conversation

@JPeer264

@JPeer264JPeer264 commented Sep 1, 2026

Copy link
Copy Markdown
Member

Ports cloudflare-agent to span streaming. The gen_ai assertions were already on streamed spans; the remaining waitForTransaction calls are replaced.

  • The worker and the Agent Durable Object both emit a GET segment for the same URL. The DO segment is picked by url.path plus a defined parent_span_id (the worker propagates its trace over the RPC binding), same discriminator the old spec used.
  • The three surviving storage spans are children of the greet rpc span, so they are selected by parent_span_id and ordered by start_timestamp; the count-based filter proof is unchanged.
  • gen_ai-to-segment relations rely on collectStreamedSpans grouping by trace (test(e2e): Group collectStreamedSpans by trace #23912): the collect finishes once the owning segment has arrived, so no manual trace_id matching is needed.

@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.12 kB+0.02%+20 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+20 B 🔺
@sentry/node - without channel injection103.42 kB+0.02%+18 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from b150a0c to 110feb6CompareSeptember 1, 2026 15:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 110feb6 to 1af4495CompareSeptember 2, 2026 06:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1af4495 to 1f6e74bCompareSeptember 2, 2026 07:10
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1f6e74b to c237b43CompareSeptember 2, 2026 07:46
@JPeer264
JPeer264 marked this pull request as ready for review September 2, 2026 07:47
transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.description === 'durable_object_storage_put')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

The trace id condition is no longer needed if you rebase develop.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here too.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const rpcSpan = spans.find(span => getSpanOp(span) === 'rpc' && span.name === 'greet')!;

const rpcSpans = (transaction.spans ?? []).filter(span => span.op === 'rpc');
const rpcSpans = spans.filter(span => getSpanOp(span) === 'rpc' && span.trace_id === rpcSpan.trace_id);

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const genAiSpan = spans.find(span => getSpanOp(span) === 'gen_ai.chat');
return (
!!genAiSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === genAiSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from c237b43 to df60649CompareSeptember 2, 2026 11:30
Comment on lines +127 to +135
const spansPromise = collectStreamedSpans('cloudflare-agent', spans =>
spans.some(
span =>
getSpanOp(span) === 'http.server' &&
span.is_segment &&
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
span.parent_span_id !== undefined,
),
);

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.

Bug: The test for internal db.query spans is ineffective because it monitors a trace that is unlikely to contain these spans, causing the test to always pass.
Severity: LOW

Suggested Fix

The test should be redesigned to capture spans from multiple, potentially unrelated traces simultaneously. Instead of resolving collectStreamedSpans on a single trace's predicate, consider a mechanism that can observe all spans received by the event proxy over a period of time, regardless of their trace_id, and then perform the assertion.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/callable.test.ts#L127-L135
Potential issue: The test at `callable.test.ts` is intended to verify that internal
`db.query` spans from the agents runtime are not emitted. It uses `collectStreamedSpans`
to listen for spans on the `http.server` trace. However, the internal database queries
for bookkeeping tables like `cf_agents_*` likely execute in a separate trace context
from the main request. Because `collectStreamedSpans` groups spans by `trace_id`, it
will only capture spans from the `http.server` trace and will never see the internal
`db.query` spans from the other trace. This causes the test to pass trivially with zero
results, rendering it ineffective at detecting regressions where these internal spans
might start leaking.

Did we get this right? 👍 / 👎 to inform future reviews.

@JPeer264
JPeer264 merged commit 3a9667e into developSep 2, 2026
46 checks passed
@JPeer264
JPeer264 deleted the jp/e2e-stream-cloudflare-agent branch September 2, 2026 12:26
Sign up for freeto 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

@JPeer264@s1gr1d@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

test(e2e): Port cloudflare-agent to span streaming - #23862

Merged
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent
Sep 2, 2026
Merged

test(e2e): Port cloudflare-agent to span streaming#23862
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent

Conversation

@JPeer264

@JPeer264JPeer264 commented Sep 1, 2026

Copy link
Copy Markdown
Member

Ports cloudflare-agent to span streaming. The gen_ai assertions were already on streamed spans; the remaining waitForTransaction calls are replaced.

  • The worker and the Agent Durable Object both emit a GET segment for the same URL. The DO segment is picked by url.path plus a defined parent_span_id (the worker propagates its trace over the RPC binding), same discriminator the old spec used.
  • The three surviving storage spans are children of the greet rpc span, so they are selected by parent_span_id and ordered by start_timestamp; the count-based filter proof is unchanged.
  • gen_ai-to-segment relations rely on collectStreamedSpans grouping by trace (test(e2e): Group collectStreamedSpans by trace #23912): the collect finishes once the owning segment has arrived, so no manual trace_id matching is needed.

@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.12 kB+0.02%+20 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+20 B 🔺
@sentry/node - without channel injection103.42 kB+0.02%+18 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from b150a0c to 110feb6CompareSeptember 1, 2026 15:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 110feb6 to 1af4495CompareSeptember 2, 2026 06:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1af4495 to 1f6e74bCompareSeptember 2, 2026 07:10
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1f6e74b to c237b43CompareSeptember 2, 2026 07:46
@JPeer264
JPeer264 marked this pull request as ready for review September 2, 2026 07:47
transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.description === 'durable_object_storage_put')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

The trace id condition is no longer needed if you rebase develop.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here too.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const rpcSpan = spans.find(span => getSpanOp(span) === 'rpc' && span.name === 'greet')!;

const rpcSpans = (transaction.spans ?? []).filter(span => span.op === 'rpc');
const rpcSpans = spans.filter(span => getSpanOp(span) === 'rpc' && span.trace_id === rpcSpan.trace_id);

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const genAiSpan = spans.find(span => getSpanOp(span) === 'gen_ai.chat');
return (
!!genAiSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === genAiSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from c237b43 to df60649CompareSeptember 2, 2026 11:30
Comment on lines +127 to +135
const spansPromise = collectStreamedSpans('cloudflare-agent', spans =>
spans.some(
span =>
getSpanOp(span) === 'http.server' &&
span.is_segment &&
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
span.parent_span_id !== undefined,
),
);

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.

Bug: The test for internal db.query spans is ineffective because it monitors a trace that is unlikely to contain these spans, causing the test to always pass.
Severity: LOW

Suggested Fix

The test should be redesigned to capture spans from multiple, potentially unrelated traces simultaneously. Instead of resolving collectStreamedSpans on a single trace's predicate, consider a mechanism that can observe all spans received by the event proxy over a period of time, regardless of their trace_id, and then perform the assertion.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/callable.test.ts#L127-L135
Potential issue: The test at `callable.test.ts` is intended to verify that internal
`db.query` spans from the agents runtime are not emitted. It uses `collectStreamedSpans`
to listen for spans on the `http.server` trace. However, the internal database queries
for bookkeeping tables like `cf_agents_*` likely execute in a separate trace context
from the main request. Because `collectStreamedSpans` groups spans by `trace_id`, it
will only capture spans from the `http.server` trace and will never see the internal
`db.query` spans from the other trace. This causes the test to pass trivially with zero
results, rendering it ineffective at detecting regressions where these internal spans
might start leaking.

Did we get this right? 👍 / 👎 to inform future reviews.

@JPeer264
JPeer264 merged commit 3a9667e into developSep 2, 2026
46 checks passed
@JPeer264
JPeer264 deleted the jp/e2e-stream-cloudflare-agent branch September 2, 2026 12:26
Sign up for freeto 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

@JPeer264@s1gr1d@andreiborza
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

test(e2e): Port cloudflare-agent to span streaming - #23862

Merged
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent
Sep 2, 2026
Merged

test(e2e): Port cloudflare-agent to span streaming#23862
JPeer264 merged 1 commit into
jp/e2e-stream-cloudflare-autoinstrumentfrom
jp/e2e-stream-cloudflare-agent

Conversation

@JPeer264

@JPeer264JPeer264 commented Sep 1, 2026

Copy link
Copy Markdown
Member

Ports cloudflare-agent to span streaming. The gen_ai assertions were already on streamed spans; the remaining waitForTransaction calls are replaced.

  • The worker and the Agent Durable Object both emit a GET segment for the same URL. The DO segment is picked by url.path plus a defined parent_span_id (the worker propagates its trace over the RPC binding), same discriminator the old spec used.
  • The three surviving storage spans are children of the greet rpc span, so they are selected by parent_span_id and ordered by start_timestamp; the count-based filter proof is unchanged.
  • gen_ai-to-segment relations rely on collectStreamedSpans grouping by trace (test(e2e): Group collectStreamedSpans by trace #23912): the collect finishes once the owning segment has arrived, so no manual trace_id matching is needed.

@github-actions

github-actionsBot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.12 kB+0.02%+20 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+20 B 🔺
@sentry/node - without channel injection103.42 kB+0.02%+18 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from b150a0c to 110feb6CompareSeptember 1, 2026 15:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 110feb6 to 1af4495CompareSeptember 2, 2026 06:21
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1af4495 to 1f6e74bCompareSeptember 2, 2026 07:10
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from 1f6e74b to c237b43CompareSeptember 2, 2026 07:46
@JPeer264
JPeer264 marked this pull request as ready for review September 2, 2026 07:47
transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.description === 'durable_object_storage_put')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

The trace id condition is no longer needed if you rebase develop.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

done

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here too.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const rpcSpan = spans.find(span => getSpanOp(span) === 'rpc' && span.name === 'greet')!;

const rpcSpans = (transaction.spans ?? []).filter(span => span.op === 'rpc');
const rpcSpans = spans.filter(span => getSpanOp(span) === 'rpc' && span.trace_id === rpcSpan.trace_id);

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

const genAiSpan = spans.find(span => getSpanOp(span) === 'gen_ai.chat');
return (
!!genAiSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === genAiSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

transactionEvent.transaction === 'webSocketMessage' &&
(transactionEvent.spans ?? []).some(span => span.op === 'rpc' && span.description === 'greet')
!!rpcSpan &&
spans.some(span => span.is_segment && span.name === 'webSocketMessage' && span.trace_id === rpcSpan.trace_id)

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.

And here.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JPeer264
JPeer264force-pushed the jp/e2e-stream-cloudflare-agent branch from c237b43 to df60649CompareSeptember 2, 2026 11:30
Comment on lines +127 to +135
const spansPromise = collectStreamedSpans('cloudflare-agent', spans =>
spans.some(
span =>
getSpanOp(span) === 'http.server' &&
span.is_segment &&
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
span.parent_span_id !== undefined,
),
);

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.

Bug: The test for internal db.query spans is ineffective because it monitors a trace that is unlikely to contain these spans, causing the test to always pass.
Severity: LOW

Suggested Fix

The test should be redesigned to capture spans from multiple, potentially unrelated traces simultaneously. Instead of resolving collectStreamedSpans on a single trace's predicate, consider a mechanism that can observe all spans received by the event proxy over a period of time, regardless of their trace_id, and then perform the assertion.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/callable.test.ts#L127-L135
Potential issue: The test at `callable.test.ts` is intended to verify that internal
`db.query` spans from the agents runtime are not emitted. It uses `collectStreamedSpans`
to listen for spans on the `http.server` trace. However, the internal database queries
for bookkeeping tables like `cf_agents_*` likely execute in a separate trace context
from the main request. Because `collectStreamedSpans` groups spans by `trace_id`, it
will only capture spans from the `http.server` trace and will never see the internal
`db.query` spans from the other trace. This causes the test to pass trivially with zero
results, rendering it ineffective at detecting regressions where these internal spans
might start leaking.

Did we get this right? 👍 / 👎 to inform future reviews.

@JPeer264
JPeer264 merged commit 3a9667e into developSep 2, 2026
46 checks passed
@JPeer264
JPeer264 deleted the jp/e2e-stream-cloudflare-agent branch September 2, 2026 12:26
Sign up for freeto 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

@JPeer264@s1gr1d@andreiborza