Auto-inject workflow run/step ID VQS headers from queue payload#1098
Conversation
Move `x-workflow-run-id` and `x-workflow-step-id` header injection into `world-vercel` queue implementation so callers don't need to set them manually. The headers are extracted from the queue payload itself. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 79da8ab The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🧪 E2E Test Results❌ Some tests failed Summary
❌ Failed Tests🌍 Community Worlds (45 failed)turso (45 failed):
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
❌ 🌍 Community Worlds
✅ 📋 Other
|
📊 Benchmark Results
workflow with no steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Nitro | Express workflow with 1 step💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express workflow with 10 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro workflow with 25 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 50 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.all with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro Promise.all with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro Promise.all with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro Promise.race with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Promise.race with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Promise.race with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
|
There was a problem hiding this comment.
Pull request overview
This PR centralizes injection of Vercel Queue (VQS) observability headers (x-workflow-run-id, x-workflow-step-id) inside @workflow/world-vercel by deriving them from the queued payload, and removes manual header passing from @workflow/core callers.
Changes:
- Add payload-derived VQS header injection in
packages/world-vercel/src/queue.tsand merge with any caller-provided headers. - Remove manual
x-workflow-run-idheader passing fromsuspension-handlerandstep-handlerqueueing paths. - Add a changeset bumping
@workflow/world-verceland@workflow/coreas patch releases.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| packages/world-vercel/src/queue.ts | Derive and inject run/step ID headers from QueuePayload during Client.send() calls. |
| packages/core/src/runtime/suspension-handler.ts | Stop manually injecting x-workflow-run-id when queueing step execution messages. |
| packages/core/src/runtime/step-handler.ts | Stop manually injecting x-workflow-run-id when re-queueing workflow invocations. |
| .changeset/auto-vqs-run-id-header.md | Record patch release notes for the header injection behavior change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| if ('workflowRunId' in payload && typeof payload.workflowRunId === 'string') { |
There was a problem hiding this comment.
getHeadersFromPayload can set x-workflow-run-id twice (from runId and workflowRunId), with the latter overwriting the former. Because QueuePayloadSchema is a union of non-strict objects (unknown keys are stripped), a payload object containing both keys can be encoded/parsed as a workflow payload with only runId, while the header ends up reflecting workflowRunId, creating an observability mismatch. Consider deriving headers from the same sanitized/encoded payload you actually send (or explicitly choosing precedence with if/else if, and potentially warning when both are present).
| } | |
| if ('workflowRunId' in payload && typeof payload.workflowRunId === 'string') { | |
| } else if ( | |
| 'workflowRunId' in payload && | |
| typeof payload.workflowRunId === 'string' | |
| ) { |
| { | ||
| idempotencyKey: opts?.idempotencyKey, | ||
| delaySeconds: opts?.delaySeconds, | ||
| headers: opts?.headers, | ||
| headers: { | ||
| ...getHeadersFromPayload(payload), | ||
| ...opts?.headers, | ||
| }, |
There was a problem hiding this comment.
New behavior auto-injects VQS headers from the payload, but there’s no test assertion covering that Client.send receives the expected x-workflow-run-id / x-workflow-step-id headers (including the delayed re-enqueue path and merge behavior with opts.headers). Adding a focused unit test in packages/world-vercel/src/queue.test.ts would prevent regressions.
Summary
x-workflow-run-idandx-workflow-step-idheader injection intoworld-vercelqueue implementation (getHeadersFromPayload) so headers are automatically derived from the queue payloadx-workflow-run-idheader passing from all callers in@workflow/core(step-handler, suspension-handler)createQueueHandlerwhich was previously missing the header entirelyTest plan
pnpm typecheckpasses (pre-existingweb-sharedfailure is unrelated)pnpm buildpasses for@workflow/coreand@workflow/world-vercel🤖 Generated with Claude Code