Repository navigation
Conversation
Keep experimentalSetAttributes as a deprecated optional alias on the World interface; callers prefer runs.setAttributes and fall back to the old name. Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: vercel-v[bot] <353008837+vercel-v[bot]@users.noreply.github.com>
🦋 Changeset detectedLatest commit: 611553b The changes in this PR will be included in the next version bump. This PR includes changesets to release 21 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 |
📊 Workflow Benchmarkscommit Backend:
📈 STSO distribution vs main (inline / queue-hop histograms)1020 steps (inline) Cumulative STSO time: main 133047ms → this run 117578ms (Δ -15469ms, -12%) 📜 Previous results (1)c8254e6Fri, 13 Nov 2026 02:49:19 GMT · run logs
ℹ️ Metric definitions & methodologyThe collapsed STSO distribution section above buckets every step gap, split inline (same warm process — pure framework overhead) vs queue-hop (fresh process — dispatch, reinit, replay). Best/P75/P90/P99 deltas compare against the most recent benchmark run on Metrics — TTFS: time to first step body (in-deployment start() → first step body) · Fan-out TTFS: fan-out time to first step (in-deployment start() → first of the parallel step bodies to complete) · Fan-out TTLS: fan-out time to last step (in-deployment start() → last of the parallel step bodies to complete, i.e. when the Promise.all resolves) · STSO: step-to-step overhead (gap between consecutive step bodies) · WO: workflow overhead (whole-run time outside step bodies, in-deployment anchored) Scenarios — step: one trivial no-op step, no stream; no hooks, so the run stays in turbo mode (in-process fast path) · stream: one streaming step; no hooks, so the run stays in turbo mode (in-process fast path) · hook + stream: registers a hook before one step, which exits turbo mode (dispatch path) · 1020 steps: 1020 trivial sequential steps; STSO is measured between consecutive steps in the given step ranges, and WO is the whole-run overhead outside step bodies · Promise.all(100 steps): 100 trivial no-op steps started together in a single Promise.all; Fan-out TTFS is the first of them to complete and Fan-out TTLS the last, both from the in-deployment clientStart, so their gap is the spread the runtime adds across the fan-out 🔴 marks a percentile over its target (within target is left unmarked). Targets (p75/p90/p99, ms) — TTFS 200/300/600 All timestamps are deployment-side; runs are triggered in-deployment, so the CI runner and api.vercel.com sit outside every measured window. TTFS = Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor. |
🧪 E2E Test Results✅ All tests passed
|
| Passed | Failed | Skipped | Total | |
|---|---|---|---|---|
| ✅ ▲ Vercel Production | 4649 | 0 | 940 | 5589 |
| ✅ 💻 Local Development | 5272 | 0 | 731 | 6003 |
| ✅ 📦 Local Production | 5272 | 0 | 731 | 6003 |
| ✅ 🐘 Local Postgres | 5417 | 0 | 586 | 6003 |
| ✅ 🪟 Windows | 401 | 0 | 13 | 414 |
| ✅ dynamic-runs | 0 | 0 | 0 | 0 |
| ✅ local-postgres-spec9 | 0 | 0 | 364 | 364 |
| ✅ local-spec9 | 409 | 0 | 5 | 414 |
| ✅ vercel-http-transport | 1054 | 0 | 188 | 1242 |
| ✅ vercel-multi-region | 27 | 0 | 0 | 27 |
| ✅ vercel-queue-hold | 0 | 0 | 0 | 0 |
| ✅ vercel-spec9 | 395 | 0 | 19 | 414 |
| ✅ vercel-ws-transport | 712 | 0 | 116 | 828 |
| Total | 23608 | 0 | 3693 | 27301 |
Details by Category
✅ ▲ Vercel Production
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ astro-node | 171 | 0 | 36 |
| ✅ astro-quickjs | 170 | 0 | 37 |
| ✅ example-node | 171 | 0 | 36 |
| ✅ example-quickjs | 170 | 0 | 37 |
| ✅ express-node | 171 | 0 | 36 |
| ✅ express-quickjs | 170 | 0 | 37 |
| ✅ fastify-node | 171 | 0 | 36 |
| ✅ fastify-quickjs | 170 | 0 | 37 |
| ✅ hono-node | 171 | 0 | 36 |
| ✅ hono-quickjs | 170 | 0 | 37 |
| ✅ nest-node | 171 | 0 | 36 |
| ✅ nest-quickjs | 170 | 0 | 37 |
| ✅ nextjs-turbopack-node | 199 | 0 | 8 |
| ✅ nextjs-turbopack-quickjs | 198 | 0 | 9 |
| ✅ nextjs-webpack-node | 199 | 0 | 8 |
| ✅ nextjs-webpack-quickjs | 198 | 0 | 9 |
| ✅ nitro-node | 171 | 0 | 36 |
| ✅ nitro-quickjs | 170 | 0 | 37 |
| ✅ nuxt-node | 171 | 0 | 36 |
| ✅ nuxt-quickjs | 170 | 0 | 37 |
| ✅ python-node | 66 | 0 | 141 |
| ✅ sveltekit-node | 190 | 0 | 17 |
| ✅ sveltekit-quickjs | 189 | 0 | 18 |
| ✅ tanstack-start-node | 171 | 0 | 36 |
| ✅ tanstack-start-quickjs | 170 | 0 | 37 |
| ✅ vite-node | 171 | 0 | 36 |
| ✅ vite-quickjs | 170 | 0 | 37 |
✅ 💻 Local Development
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ astro-stable-node | 172 | 0 | 35 |
| ✅ astro-stable-quickjs | 171 | 0 | 36 |
| ✅ express-stable-node | 172 | 0 | 35 |
| ✅ express-stable-quickjs | 171 | 0 | 36 |
| ✅ fastify-stable-node | 172 | 0 | 35 |
| ✅ fastify-stable-quickjs | 171 | 0 | 36 |
| ✅ hono-stable-node | 172 | 0 | 35 |
| ✅ hono-stable-quickjs | 171 | 0 | 36 |
| ✅ nest-stable-node | 172 | 0 | 35 |
| ✅ nest-stable-quickjs | 171 | 0 | 36 |
| ✅ nextjs-turbopack-canary-node | 201 | 0 | 6 |
| ✅ nextjs-turbopack-canary-quickjs | 200 | 0 | 7 |
| ✅ nextjs-turbopack-quickjs-snapshot | 200 | 0 | 7 |
| ✅ nextjs-turbopack-stable-node | 201 | 0 | 6 |
| ✅ nextjs-turbopack-stable-quickjs | 200 | 0 | 7 |
| ✅ nextjs-webpack-canary-node | 201 | 0 | 6 |
| ✅ nextjs-webpack-canary-quickjs | 200 | 0 | 7 |
| ✅ nextjs-webpack-stable-node | 201 | 0 | 6 |
| ✅ nextjs-webpack-stable-quickjs | 200 | 0 | 7 |
| ✅ nitro-stable-node | 172 | 0 | 35 |
| ✅ nitro-stable-quickjs | 171 | 0 | 36 |
| ✅ nuxt-stable-node | 172 | 0 | 35 |
| ✅ nuxt-stable-quickjs | 171 | 0 | 36 |
| ✅ sveltekit-stable-node | 191 | 0 | 16 |
| ✅ sveltekit-stable-quickjs | 190 | 0 | 17 |
| ✅ tanstack-start-node | 172 | 0 | 35 |
| ✅ tanstack-start-quickjs | 171 | 0 | 36 |
| ✅ vite-stable-node | 172 | 0 | 35 |
| ✅ vite-stable-quickjs | 171 | 0 | 36 |
✅ 📦 Local Production
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ astro-stable-node | 172 | 0 | 35 |
| ✅ astro-stable-quickjs | 171 | 0 | 36 |
| ✅ express-stable-node | 172 | 0 | 35 |
| ✅ express-stable-quickjs | 171 | 0 | 36 |
| ✅ fastify-stable-node | 172 | 0 | 35 |
| ✅ fastify-stable-quickjs | 171 | 0 | 36 |
| ✅ hono-stable-node | 172 | 0 | 35 |
| ✅ hono-stable-quickjs | 171 | 0 | 36 |
| ✅ nest-stable-node | 172 | 0 | 35 |
| ✅ nest-stable-quickjs | 171 | 0 | 36 |
| ✅ nextjs-turbopack-canary-node | 201 | 0 | 6 |
| ✅ nextjs-turbopack-canary-quickjs | 200 | 0 | 7 |
| ✅ nextjs-turbopack-quickjs-snapshot | 200 | 0 | 7 |
| ✅ nextjs-turbopack-stable-node | 201 | 0 | 6 |
| ✅ nextjs-turbopack-stable-quickjs | 200 | 0 | 7 |
| ✅ nextjs-webpack-canary-node | 201 | 0 | 6 |
| ✅ nextjs-webpack-canary-quickjs | 200 | 0 | 7 |
| ✅ nextjs-webpack-stable-node | 201 | 0 | 6 |
| ✅ nextjs-webpack-stable-quickjs | 200 | 0 | 7 |
| ✅ nitro-stable-node | 172 | 0 | 35 |
| ✅ nitro-stable-quickjs | 171 | 0 | 36 |
| ✅ nuxt-stable-node | 172 | 0 | 35 |
| ✅ nuxt-stable-quickjs | 171 | 0 | 36 |
| ✅ sveltekit-stable-node | 191 | 0 | 16 |
| ✅ sveltekit-stable-quickjs | 190 | 0 | 17 |
| ✅ tanstack-start-node | 172 | 0 | 35 |
| ✅ tanstack-start-quickjs | 171 | 0 | 36 |
| ✅ vite-stable-node | 172 | 0 | 35 |
| ✅ vite-stable-quickjs | 171 | 0 | 36 |
✅ 🐘 Local Postgres
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ astro-stable-node | 177 | 0 | 30 |
| ✅ astro-stable-quickjs | 176 | 0 | 31 |
| ✅ express-stable-node | 177 | 0 | 30 |
| ✅ express-stable-quickjs | 176 | 0 | 31 |
| ✅ fastify-stable-node | 177 | 0 | 30 |
| ✅ fastify-stable-quickjs | 176 | 0 | 31 |
| ✅ hono-stable-node | 177 | 0 | 30 |
| ✅ hono-stable-quickjs | 176 | 0 | 31 |
| ✅ nest-stable-node | 177 | 0 | 30 |
| ✅ nest-stable-quickjs | 176 | 0 | 31 |
| ✅ nextjs-turbopack-canary-node | 206 | 0 | 1 |
| ✅ nextjs-turbopack-canary-quickjs | 205 | 0 | 2 |
| ✅ nextjs-turbopack-quickjs-snapshot | 205 | 0 | 2 |
| ✅ nextjs-turbopack-stable-node | 206 | 0 | 1 |
| ✅ nextjs-turbopack-stable-quickjs | 205 | 0 | 2 |
| ✅ nextjs-webpack-canary-node | 206 | 0 | 1 |
| ✅ nextjs-webpack-canary-quickjs | 205 | 0 | 2 |
| ✅ nextjs-webpack-stable-node | 206 | 0 | 1 |
| ✅ nextjs-webpack-stable-quickjs | 205 | 0 | 2 |
| ✅ nitro-stable-node | 177 | 0 | 30 |
| ✅ nitro-stable-quickjs | 176 | 0 | 31 |
| ✅ nuxt-stable-node | 177 | 0 | 30 |
| ✅ nuxt-stable-quickjs | 176 | 0 | 31 |
| ✅ sveltekit-stable-node | 196 | 0 | 11 |
| ✅ sveltekit-stable-quickjs | 195 | 0 | 12 |
| ✅ tanstack-start-node | 177 | 0 | 30 |
| ✅ tanstack-start-quickjs | 176 | 0 | 31 |
| ✅ vite-stable-node | 177 | 0 | 30 |
| ✅ vite-stable-quickjs | 176 | 0 | 31 |
✅ 🪟 Windows
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ nextjs-turbopack-node | 201 | 0 | 6 |
| ✅ nextjs-turbopack-quickjs | 200 | 0 | 7 |
✅ dynamic-runs
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ astro-vercel | 0 | 0 | 0 |
| ✅ example-vercel | 0 | 0 | 0 |
| ✅ express-vercel | 0 | 0 | 0 |
| ✅ fastify-vercel | 0 | 0 | 0 |
| ✅ hono-vercel | 0 | 0 | 0 |
| ✅ nest-vercel | 0 | 0 | 0 |
| ✅ nextjs-turbopack-vercel | 0 | 0 | 0 |
| ✅ nextjs-webpack-vercel | 0 | 0 | 0 |
| ✅ nitro-vercel | 0 | 0 | 0 |
| ✅ nuxt-vercel | 0 | 0 | 0 |
| ✅ sveltekit-vercel | 0 | 0 | 0 |
| ✅ tanstack-start-vercel | 0 | 0 | 0 |
| ✅ vite-vercel | 0 | 0 | 0 |
✅ local-postgres-spec9
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ nextjs-turbopack-stable-node | 0 | 0 | 182 |
| ✅ nextjs-turbopack-stable-quickjs | 0 | 0 | 182 |
✅ local-spec9
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ nextjs-turbopack-node | 205 | 0 | 2 |
| ✅ nextjs-turbopack-quickjs | 204 | 0 | 3 |
✅ vercel-http-transport
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ example | 171 | 0 | 36 |
| ✅ express | 171 | 0 | 36 |
| ✅ hono | 171 | 0 | 36 |
| ✅ nextjs-turbopack | 199 | 0 | 8 |
| ✅ nitro | 171 | 0 | 36 |
| ✅ vite | 171 | 0 | 36 |
✅ vercel-multi-region
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ nextjs-turbopack | 27 | 0 | 0 |
✅ vercel-queue-hold
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ nextjs-turbopack | 0 | 0 | 0 |
✅ vercel-spec9
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ nextjs-turbopack-node | 198 | 0 | 9 |
| ✅ nextjs-turbopack-quickjs | 197 | 0 | 10 |
✅ vercel-ws-transport
| App | Passed | Failed | Skipped |
|---|---|---|---|
| ✅ example | 171 | 0 | 36 |
| ✅ express | 171 | 0 | 36 |
| ✅ nextjs-turbopack | 199 | 0 | 8 |
| ✅ vite | 171 | 0 | 36 |
Sim WorldSimulated world deterministic testing for races. Traces 🟢 world-sim scenario book — 0 fail of 45 total
Full trace: |
About these numbersSizes are gzip; parentheses show the change against
|
The current SDK writes attributes as attr_set events and never calls the method. Revert the rename, mark the method and ExperimentalSetAttributesResult deprecated, and remove the unreachable __builtin_set_attributes bridge. First-party World implementations stay for workflow 5.0.0-beta.8 to beta.14. Signed-off-by: Pranay Prakash <pranay.gp@gmail.com> Co-authored-by: vercel-v[bot] <353008837+vercel-v[bot]@users.noreply.github.com>
Deprecates the optional World method
runs.experimentalSetAttributes(andExperimentalSetAttributesResult) instead of renaming it, and removes the__builtin_set_attributesstep bridge, its only caller. Nothing in the current SDK can reach either:setAttributes()writesattr_setevents throughevents.create.Why
The method looked like part of the attributes API, and the World docs said run attributes depend on it. They don't. Since
workflow5.0.0-beta.15 (#2226),setAttributes()andstart(..., { attributes })write through the event log. The direct write survived only as a dead step and an optional interface member that new World authors were told to implement.Reachability (why it is dead for the current SDK)
__builtin_set_attributeswas referenced only by its own definition. The workflow-body path dispatchesWORKFLOW_SET_ATTRIBUTEStocreateSetAttributes(core/src/workflow.ts:719-724), and the QuickJS / spec 9 engine does the same with a pendingattributeop (core/src/runtime/quickjs-runtime.ts:1066). The step-body path callsevents.createwithattr_set(core/src/set-attributes.ts). Neither callsuseStep('__builtin_set_attributes')orruns.experimentalSetAttributes.eventCountfrom the loaded log throughmaxEventSlot→requireEventSlot, which throws on a non-slot ID (core/src/runtime/helpers.ts:980-989,slotSnapshotParamsat:1201,spec8/suspension-handler.ts:599). Theupgrading-to-v5callout already documents that pre-v5 runs must drain on their old build. Even if a replay got that far, the current VM writesattr_setwhere the log recorded astep_created, so it would not call the step.__wkf_step_*topics, which were retired in 5.0.0-beta.37 (Remove retired v1 step route plumbing #3061) and are not consumed by current Worlds. On Vercel, messages and runs stay on the deployment that created them.Why the first-party implementations stay
Old SDKs can still call the method.
workflow5.0.0-beta.8 to beta.14 callruns.experimentalSetAttributes(the workflow-body bridge, and step bodies from beta.9).workflow→@workflow/core→world-local/world-vercelare exact pins (workspace:*), so the default pairing is always same-release. Butworld-postgresdeclares no peer dependency onworkflowor@workflow/core, any World can be selected by package name throughWORKFLOW_TARGET_WORLD, and those betas have no World spec-range check (core/src/runtime/world-compatibility.tsdoesn't exist there). So a newer World package can be installed next to one of them.world-local,world-postgresandworld-vercelare left unchanged, and the/v2/runs/:runId/attributesendpoint is untouched.Changes
@workflow/world:runs.experimentalSetAttributesandExperimentalSetAttributesResultare marked@deprecated. The JSDoc says the SDK no longer calls the method, attributes areattr_set/run_createdevents, new Worlds should not implement it, and which releases were the last callers.workflow: removes__builtin_set_attributesand its test.core: adds a test that a step-bodysetAttributes()writesattr_setand never callsruns.experimentalSetAttributes, even when the World has it.building-a-worlddrops the method from the Storage interface and adds a short "Run attributes" rule (validate and materializeattr_set/run_createdattributes atomically; rejectattr_seton a terminal run).upgrading-to-v5drops the row claiming attributes depend on the method.packages/core/src/attributes-mvp.mdrecords the retirement, and both migration skills (versions bumped) stop recommending the method.@workflow/worldpatch (deprecation),workflowpatch (bridge removal).Validation
tsc --noEmitpasses inworld,world-local,world-postgres,world-vercel,workflow,core.world: 300world-local: 637world-vercel: 1194workflow: 10coreattribute tests: 34world-postgrestest/storage.test.ts: 183, against Postgres in Dockercoresuite: 4594 passed (6 expected fail, 24 skipped).world-testingandworld-postgres'sspec.test.ts(both need the Rust-built SWC plugin), the rest of theworld-postgressuite (container-heavy), and e2e.