Skip to content

Add native v4 workflow attribute events#2226

Draft
pranaygp wants to merge 3 commits into
mainfrom
pranaygp/codex/native-attributes-client
Draft

Add native v4 workflow attribute events#2226
pranaygp wants to merge 3 commits into
mainfrom
pranaygp/codex/native-attributes-client

Conversation

@pranaygp
Copy link
Copy Markdown
Contributor

@pranaygp pranaygp commented Jun 2, 2026

Summary

  • replace the experimental built-in-step attribute bridge with native v4 attr_set events in workflow and step contexts
  • add start(..., { attributes }) so initial run attributes are seeded during creation
  • materialize native events in local and Postgres worlds with correlated workflow-write deduplication and native terminal-run rejection

Rollout

  • Server support must deploy first: vercel/workflow-server#469 adds the native event support while retaining the deprecated direct attributes endpoint for legacy clients.
  • The new SDK path intentionally requires World spec v4; it does not fall back to the MVP endpoint on v3 worlds.
  • Workflow-originated writes are correlated one-shot events; step-originated writes remain repeatable.

Testing

  • pnpm --filter @workflow/core test
  • pnpm --filter @workflow/world-local test
  • cd packages/world-postgres && pnpm vitest run test/storage.test.ts
  • pnpm exec turbo run build --filter=nextjs-turbopack... --output-logs=errors-only
  • DEPLOYMENT_URL=http://localhost:3000 APP_NAME=nextjs-turbopack pnpm vitest run packages/core/e2e/e2e.test.ts -t experimental_setAttributes

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Jun 2, 2026

🦋 Changeset detected

Latest commit: 17ccd34

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 21 packages
Name Type
workflow Minor
@workflow/core Minor
@workflow/world Minor
@workflow/world-local Minor
@workflow/world-postgres Minor
@workflow/ai Major
@workflow/world-testing Patch
@workflow/builders Patch
@workflow/cli Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/vitest Patch
@workflow/web-shared Patch
@workflow/web Patch
@workflow/world-vercel Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch
@workflow/nuxt Patch

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

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Jun 2, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Jun 2, 2026 9:24pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Jun 2, 2026 9:24pm
example-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workbench-astro-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workbench-express-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workbench-fastify-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workbench-hono-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workbench-nitro-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workbench-nuxt-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workbench-tanstack-start-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workbench-vite-workflow Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workflow-docs Ready Ready Preview, Comment, Open in v0 Jun 2, 2026 9:24pm
workflow-swc-playground Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workflow-tarballs Ready Ready Preview, Comment Jun 2, 2026 9:24pm
workflow-web Ready Ready Preview, Comment Jun 2, 2026 9:24pm

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

📊 Benchmark Results

📈 Comparing against baseline from main branch. Green 🟢 = faster, Red 🔺 = slower.

workflow with no steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 0.042s (-0.7%) 1.007s (~) 0.965s 10 1.00x
💻 Local Nitro 0.045s (+10.1% 🔺) 1.005s (~) 0.961s 10 1.08x
🐘 Postgres Nitro 0.061s (-3.3%) 1.011s (~) 0.950s 10 1.47x
🐘 Postgres Express 0.064s (+0.8%) 1.012s (~) 0.948s 10 1.54x
🐘 Postgres Next.js (Turbopack) 0.069s (-1.7%) 1.012s (~) 0.943s 10 1.65x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 0.307s (+10.0% 🔺) 2.401s (+13.4% 🔺) 2.094s 10 1.00x
▲ Vercel Next.js (Turbopack) 0.335s (+13.2% 🔺) 2.444s (+14.2% 🔺) 2.109s 10 1.09x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 1 step

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 1.093s (~) 2.006s (~) 0.913s 10 1.00x
💻 Local Nitro 1.098s (+0.8%) 2.006s (~) 0.907s 10 1.00x
🐘 Postgres Express 1.106s (~) 2.010s (~) 0.904s 10 1.01x
🐘 Postgres Nitro 1.108s (-0.6%) 2.009s (~) 0.902s 10 1.01x
🐘 Postgres Next.js (Turbopack) 1.147s (~) 2.009s (~) 0.863s 10 1.05x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 1.705s (+6.9% 🔺) 3.659s (+14.4% 🔺) 1.954s 10 1.00x
▲ Vercel Nitro 1.819s (+4.0%) 3.905s (+1.7%) 2.086s 10 1.07x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 10 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 10.534s (~) 11.023s (~) 0.488s 3 1.00x
🐘 Postgres Nitro 10.564s (~) 11.015s (~) 0.451s 3 1.00x
🐘 Postgres Express 10.573s (~) 11.017s (~) 0.444s 3 1.00x
💻 Local Nitro 10.585s (+0.7%) 11.022s (~) 0.437s 3 1.00x
🐘 Postgres Next.js (Turbopack) 10.897s (+0.5%) 11.019s (~) 0.121s 3 1.03x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 13.460s (-25.9% 🟢) 15.485s (-21.2% 🟢) 2.025s 2 1.00x
▲ Vercel Nitro 13.684s (-6.3% 🟢) 16.061s (-3.1%) 2.377s 2 1.02x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 25 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 13.703s (-1.0%) 14.018s (~) 0.315s 5 1.00x
💻 Local Express 13.746s (~) 14.027s (~) 0.281s 5 1.00x
💻 Local Nitro 13.788s (+0.7%) 14.027s (~) 0.239s 5 1.01x
🐘 Postgres Express 13.815s (~) 14.019s (~) 0.204s 5 1.01x
🐘 Postgres Next.js (Turbopack) 14.436s (-2.3%) 15.014s (-1.7%) 0.578s 4 1.05x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 20.970s (+1.2%) 23.214s (+1.8%) 2.244s 3 1.00x
▲ Vercel Next.js (Turbopack) 23.743s (+2.1%) 25.552s (+5.3% 🔺) 1.809s 3 1.13x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 12.407s (-1.5%) 13.019s (~) 0.612s 7 1.00x
💻 Local Nitro 12.516s (+0.6%) 13.026s (~) 0.511s 7 1.01x
💻 Local Express 12.531s (+0.8%) 13.026s (~) 0.494s 7 1.01x
🐘 Postgres Express 12.654s (+0.6%) 13.018s (~) 0.364s 7 1.02x
🐘 Postgres Next.js (Turbopack) 13.795s (~) 14.018s (~) 0.223s 7 1.11x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 31.214s (+13.3% 🔺) 33.728s (+14.8% 🔺) 2.514s 3 1.00x
▲ Vercel Next.js (Turbopack) 31.983s (+7.0% 🔺) 34.493s (+11.0% 🔺) 2.510s 3 1.02x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.187s (-3.8%) 2.006s (~) 0.818s 15 1.00x
🐘 Postgres Express 1.189s (~) 2.007s (~) 0.819s 15 1.00x
🐘 Postgres Nitro 1.196s (+0.8%) 2.008s (~) 0.811s 15 1.01x
🐘 Postgres Next.js (Turbopack) 1.236s (-1.8%) 2.008s (~) 0.772s 15 1.04x
💻 Local Express 1.337s (+10.9% 🔺) 2.006s (~) 0.670s 15 1.13x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.631s (+9.3% 🔺) 4.537s (+11.5% 🔺) 1.906s 7 1.00x
▲ Vercel Next.js (Turbopack) 3.170s (+13.6% 🔺) 5.182s (+30.1% 🔺) 2.012s 6 1.20x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.all with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.246s (~) 2.008s (~) 0.762s 15 1.00x
🐘 Postgres Express 1.256s (+0.5%) 2.008s (~) 0.752s 15 1.01x
🐘 Postgres Next.js (Turbopack) 1.404s (~) 2.008s (~) 0.604s 15 1.13x
💻 Local Express 1.751s (-0.8%) 2.006s (-3.2%) 0.255s 15 1.41x
💻 Local Nitro 1.835s (+7.3% 🔺) 2.074s (+3.4%) 0.239s 15 1.47x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 3.976s (-1.9%) 5.712s (+6.3% 🔺) 1.736s 6 1.00x
▲ Vercel Nitro 4.452s (+35.2% 🔺) 6.318s (+22.3% 🔺) 1.867s 5 1.12x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.all with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.377s (-0.9%) 2.007s (~) 0.631s 15 1.00x
🐘 Postgres Express 1.383s (-0.8%) 2.007s (~) 0.625s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.742s (+2.0%) 2.317s (+11.6% 🔺) 0.575s 13 1.27x
💻 Local Express 5.163s (+16.3% 🔺) 5.680s (+13.3% 🔺) 0.517s 6 3.75x
💻 Local Nitro 5.166s (+15.2% 🔺) 5.681s (+13.4% 🔺) 0.515s 6 3.75x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 5.513s (+4.9%) 7.377s (+9.6% 🔺) 1.864s 5 1.00x
▲ Vercel Nitro 8.868s (+79.0% 🔺) 10.968s (+70.0% 🔺) 2.100s 3 1.61x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 10 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.189s (+0.5%) 2.008s (~) 0.819s 15 1.00x
🐘 Postgres Express 1.193s (~) 2.009s (~) 0.815s 15 1.00x
🐘 Postgres Next.js (Turbopack) 1.256s (~) 2.007s (~) 0.751s 15 1.06x
💻 Local Nitro 1.583s (+4.1%) 2.006s (~) 0.422s 15 1.33x
💻 Local Express 1.595s (~) 2.006s (~) 0.411s 15 1.34x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 2.793s (-2.1%) 4.447s (~) 1.654s 7 1.00x
▲ Vercel Nitro 2.852s (-13.4% 🟢) 4.790s (-3.9%) 1.938s 7 1.02x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

Promise.race with 25 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.262s (-5.6% 🟢) 2.008s (-3.3%) 0.746s 15 1.00x
🐘 Postgres Express 1.271s (+0.5%) 2.009s (~) 0.737s 15 1.01x
🐘 Postgres Next.js (Turbopack) 1.370s (-2.2%) 2.007s (~) 0.637s 15 1.09x
💻 Local Express 2.101s (+2.1%) 2.591s (+3.4%) 0.490s 12 1.67x
💻 Local Nitro 2.117s (-1.2%) 2.591s (-8.3% 🟢) 0.474s 12 1.68x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 3.933s (+21.0% 🔺) 6.132s (+18.7% 🔺) 2.199s 5 1.00x
▲ Vercel Next.js (Turbopack) 4.186s (+22.8% 🔺) 5.699s (+22.9% 🔺) 1.513s 6 1.06x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Promise.race with 50 concurrent steps

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.384s (-2.4%) 2.009s (~) 0.625s 15 1.00x
🐘 Postgres Express 1.403s (-1.0%) 2.007s (~) 0.604s 15 1.01x
🐘 Postgres Next.js (Turbopack) 1.782s (-1.8%) 2.152s (-19.6% 🟢) 0.370s 14 1.29x
💻 Local Nitro 6.106s (+13.1% 🔺) 6.417s (+6.7% 🔺) 0.311s 5 4.41x
💻 Local Express 6.361s (+12.7% 🔺) 7.218s (+16.1% 🔺) 0.857s 5 4.60x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 5.211s (-38.6% 🟢) 7.284s (-30.6% 🟢) 2.073s 5 1.00x
▲ Vercel Next.js (Turbopack) 5.261s (-15.9% 🟢) 7.804s (-1.1%) 2.543s 4 1.01x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.573s (-3.5%) 1.023s (~) 0.451s 59 1.00x
🐘 Postgres Express 0.591s (~) 1.023s (~) 0.431s 59 1.03x
💻 Local Express 0.615s (+3.9%) 1.022s (+1.7%) 0.407s 59 1.07x
💻 Local Nitro 0.698s (+18.9% 🔺) 1.094s (+8.9% 🔺) 0.396s 56 1.22x
🐘 Postgres Next.js (Turbopack) 0.827s (-1.3%) 1.024s (+1.6%) 0.197s 59 1.44x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 5.435s (-9.6% 🟢) 7.331s (~) 1.896s 9 1.00x
▲ Vercel Nitro 5.561s (+8.0% 🔺) 7.725s (+17.2% 🔺) 2.164s 8 1.02x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

workflow with 25 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Express 1.384s (~) 2.054s (+1.2%) 0.670s 44 1.00x
🐘 Postgres Nitro 1.392s (+3.0%) 2.076s (+3.4%) 0.684s 44 1.01x
💻 Local Express 1.502s (-1.2%) 2.006s (~) 0.504s 45 1.09x
💻 Local Nitro 1.556s (+5.2% 🔺) 2.006s (~) 0.451s 45 1.12x
🐘 Postgres Next.js (Turbopack) 1.968s (+0.5%) 2.203s (~) 0.235s 41 1.42x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 13.653s (+3.9%) 16.204s (+6.3% 🔺) 2.551s 6 1.00x
▲ Vercel Next.js (Turbopack) 14.887s (+11.2% 🔺) 17.151s (+15.3% 🔺) 2.265s 6 1.09x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 sequential data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 2.600s (-4.4%) 3.033s (-1.7%) 0.433s 40 1.00x
🐘 Postgres Express 2.725s (~) 3.112s (-0.8%) 0.387s 39 1.05x
💻 Local Express 3.280s (+1.4%) 4.009s (~) 0.729s 30 1.26x
💻 Local Nitro 3.366s (+5.1% 🔺) 4.010s (~) 0.644s 30 1.29x
🐘 Postgres Next.js (Turbopack) 3.847s (-1.7%) 4.009s (-2.5%) 0.163s 30 1.48x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 27.125s (-5.6% 🟢) 30.068s (-2.4%) 2.944s 5 1.00x
▲ Vercel Next.js (Turbopack) 28.374s (-7.9% 🟢) 30.768s (-4.3%) 2.394s 4 1.05x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 10 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.209s (-4.1%) 1.006s (~) 0.797s 60 1.00x
🐘 Postgres Express 0.217s (-6.3% 🟢) 1.006s (~) 0.789s 60 1.04x
🐘 Postgres Next.js (Turbopack) 0.268s (-6.3% 🟢) 1.006s (~) 0.738s 60 1.28x
💻 Local Nitro 0.463s (~) 1.022s (+1.7%) 0.558s 59 2.22x
💻 Local Express 0.520s (+20.0% 🔺) 1.095s (+9.1% 🔺) 0.576s 55 2.49x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.980s (-3.9%) 4.727s (+0.7%) 1.747s 13 1.00x
▲ Vercel Next.js (Turbopack) 3.218s (+44.6% 🔺) 4.982s (+38.7% 🔺) 1.763s 13 1.08x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 25 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.344s (-1.1%) 1.006s (~) 0.662s 90 1.00x
🐘 Postgres Express 0.346s (-3.9%) 1.006s (~) 0.661s 90 1.00x
🐘 Postgres Next.js (Turbopack) 0.485s (-1.1%) 1.006s (~) 0.522s 90 1.41x
💻 Local Express 2.091s (~) 2.654s (+2.9%) 0.563s 34 6.08x
💻 Local Nitro 2.145s (+0.6%) 2.685s (-1.8%) 0.540s 34 6.24x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 4.872s (-7.1% 🟢) 6.771s (-0.8%) 1.900s 14 1.00x
▲ Vercel Next.js (Turbopack) 44.097s (+666.3% 🔺) 45.975s (+537.1% 🔺) 1.878s 8 9.05x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

workflow with 50 concurrent data payload steps (10KB)

💻 Local Development

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.652s (-6.0% 🟢) 1.006s (~) 0.354s 120 1.00x
🐘 Postgres Express 0.675s (-1.8%) 1.006s (~) 0.331s 120 1.04x
🐘 Postgres Next.js (Turbopack) 0.955s (-3.1%) 1.490s (-7.4% 🟢) 0.534s 81 1.47x
💻 Local Express 9.496s (~) 10.027s (-1.6%) 0.531s 12 14.56x
💻 Local Nitro 9.740s (~) 10.277s (+0.8%) 0.537s 12 14.94x
💻 Local Next.js (Turbopack) ⚠️ missing - - - -

▲ Production (Vercel)

World Framework Workflow Time Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 13.017s (-22.2% 🟢) 15.494s (-18.8% 🟢) 2.477s 8 1.00x
▲ Vercel Next.js (Turbopack) 13.909s (-17.4% 🟢) 16.249s (-12.2% 🟢) 2.341s 8 1.07x
▲ Vercel Express ⚠️ missing - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

Stream Benchmarks (includes TTFB metrics)
workflow with stream

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Nitro 1.168s (~) 2.005s (~) 0.013s (+22.1% 🔺) 2.019s (~) 0.852s 10 1.00x
🐘 Postgres Express 1.168s (-0.5%) 1.998s (~) 0.001s (-12.5% 🟢) 2.012s (~) 0.843s 10 1.00x
💻 Local Express 1.170s (~) 2.005s (~) 0.011s (-10.3% 🟢) 2.018s (~) 0.848s 10 1.00x
🐘 Postgres Nitro 1.181s (+1.4%) 2.002s (~) 0.001s (-29.4% 🟢) 2.012s (~) 0.831s 10 1.01x
🐘 Postgres Next.js (Turbopack) 1.232s (+0.7%) 2.001s (~) 0.001s (-6.7% 🟢) 2.010s (~) 0.778s 10 1.06x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 2.325s (+15.4% 🔺) 3.453s (+5.5% 🔺) 1.627s (+141.3% 🔺) 5.688s (+28.5% 🔺) 3.363s 10 1.00x
▲ Vercel Next.js (Turbopack) 2.454s (-4.2%) 3.772s (+5.5% 🔺) 1.391s (+10.2% 🔺) 5.643s (+8.2% 🔺) 3.189s 10 1.06x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

stream pipeline with 5 transform steps (1MB)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
💻 Local 🥇 Express 1.564s (-1.4%) 2.009s (~) 0.011s (+16.9% 🔺) 2.022s (~) 0.457s 30 1.00x
🐘 Postgres Nitro 1.565s (-1.9%) 2.002s (~) 0.004s (-5.8% 🟢) 2.025s (~) 0.460s 30 1.00x
🐘 Postgres Express 1.601s (~) 2.007s (~) 0.004s (+3.6%) 2.025s (~) 0.424s 30 1.02x
🐘 Postgres Next.js (Turbopack) 1.760s (~) 2.010s (~) 0.004s (+12.6% 🔺) 2.027s (~) 0.267s 30 1.13x
💻 Local Nitro 1.774s (+12.5% 🔺) 2.010s (~) 0.009s (-9.5% 🟢) 2.200s (+8.8% 🔺) 0.426s 28 1.13x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 5.955s (-28.3% 🟢) 7.652s (-25.1% 🟢) 0.244s (-20.1% 🟢) 8.436s (-23.8% 🟢) 2.481s 8 1.00x
▲ Vercel Next.js (Turbopack) 6.480s (-7.1% 🟢) 8.110s (-3.2%) 0.196s (-5.4% 🟢) 9.136s (+0.9%) 2.656s 7 1.09x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

10 parallel streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 0.718s (-0.9%) 1.067s (+3.5%) 0.000s (+3.6%) 1.079s (+2.8%) 0.361s 56 1.00x
🐘 Postgres Express 0.761s (+3.9%) 1.087s (+1.8%) 0.000s (+52.7% 🔺) 1.098s (+1.7%) 0.338s 55 1.06x
🐘 Postgres Next.js (Turbopack) 0.840s (-2.0%) 1.090s (~) 0.000s (+Infinity% 🔺) 1.103s (~) 0.263s 55 1.17x
💻 Local Nitro 1.376s (-3.2%) 2.013s (~) 0.001s (+128.6% 🔺) 2.015s (~) 0.639s 30 1.92x
💻 Local Express 1.402s (+3.9%) 2.014s (~) 0.000s (-16.7% 🟢) 2.016s (~) 0.614s 30 1.95x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Nitro 4.036s (+6.0% 🔺) 5.704s (+8.6% 🔺) 0.000s (-100.0% 🟢) 6.400s (+10.4% 🔺) 2.364s 10 1.00x
▲ Vercel Next.js (Turbopack) 4.049s (+16.8% 🔺) 5.255s (+22.7% 🔺) 0.000s (+30.0% 🔺) 6.077s (+25.5% 🔺) 2.027s 10 1.00x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Nitro | Next.js (Turbopack)

fan-out fan-in 10 streams (1MB each)

💻 Local Development

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
🐘 Postgres 🥇 Nitro 1.392s (-1.6%) 2.101s (~) 0.000s (NaN%) 2.131s (~) 0.739s 29 1.00x
🐘 Postgres Express 1.465s (-7.1% 🟢) 2.140s (-7.1% 🟢) 0.000s (-7.1% 🟢) 2.165s (-6.8% 🟢) 0.700s 28 1.05x
🐘 Postgres Next.js (Turbopack) 1.798s (+2.8%) 2.388s (+3.3%) 0.000s (-100.0% 🟢) 2.418s (+3.9%) 0.620s 26 1.29x
💻 Local Nitro 3.109s (-1.6%) 3.840s (-1.6%) 0.001s (+71.4% 🔺) 3.843s (-1.6%) 0.734s 16 2.23x
💻 Local Express 3.158s (-9.5% 🟢) 3.901s (~) 0.000s (-45.3% 🟢) 3.904s (-7.7% 🟢) 0.747s 16 2.27x
💻 Local Next.js (Turbopack) ⚠️ missing - - - - -

▲ Production (Vercel)

World Framework Workflow Time TTFB Slurp Wall Time Overhead Samples vs Fastest
▲ Vercel 🥇 Next.js (Turbopack) 6.498s (-6.8% 🟢) 8.158s (~) 0.000s (NaN%) 8.999s (+4.4%) 2.501s 7 1.00x
▲ Vercel Nitro 6.510s (-12.6% 🟢) 8.032s (-10.1% 🟢) 0.000s (+Infinity% 🔺) 8.572s (-10.0% 🟢) 2.062s 8 1.00x
▲ Vercel Express ⚠️ missing - - - - -

🔍 Observability: Next.js (Turbopack) | Nitro

Summary

Fastest Framework by World

Winner determined by most benchmark wins

World 🥇 Fastest Framework Wins
💻 Local Express 13/21
🐘 Postgres Nitro 17/21
▲ Vercel Nitro 14/21
Fastest World by Framework

Winner determined by most benchmark wins

Framework 🥇 Fastest World Wins
Express 🐘 Postgres 15/21
Next.js (Turbopack) 🐘 Postgres 21/21
Nitro 🐘 Postgres 17/21
Column Definitions
  • Workflow Time: Runtime reported by workflow (completedAt - createdAt) - primary metric
  • TTFB: Time to First Byte - time from workflow start until first stream byte received (stream benchmarks only)
  • Slurp: Time from first byte to complete stream consumption (stream benchmarks only)
  • Wall Time: Total testbench time (trigger workflow + poll for result)
  • Overhead: Testbench overhead (Wall Time - Workflow Time)
  • Samples: Number of benchmark iterations run
  • vs Fastest: How much slower compared to the fastest configuration for this benchmark

Worlds:

  • 💻 Local: In-memory filesystem world (local development)
  • 🐘 Postgres: PostgreSQL database world (local development)
  • ▲ Vercel: Vercel production/preview deployment
  • 🌐 Turso: Community world (local development)
  • 🌐 MongoDB: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Jazz: Community world (local development)
  • 🌐 Redis: Community world (local development)
  • 🌐 Redis + BullMQ: Community world (local development)
  • 🌐 Cloudflare: Community world (local development)
  • 🌐 MySQL: Community world (local development)
  • 🌐 Azure: Community world (local development)
  • 🌐 NATS JetStream: Community world (local development)
  • 🌐 Upstash: Community world (local development)

📋 View full workflow run


Some benchmark jobs failed:

  • Local: failure
  • Postgres: success
  • Vercel: failure

Check the workflow run for details.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
❌ ▲ Vercel Production 1220 68 219 1507
✅ 💻 Local Development 1699 0 219 1918
✅ 📦 Local Production 1699 0 219 1918
✅ 🐘 Local Postgres 1699 0 219 1918
✅ 🪟 Windows 137 0 0 137
❌ 📋 Other 777 6 176 959
Total 7231 74 1052 8357

❌ Failed Tests

▲ Vercel Production (68 failed)

astro (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

example (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

express (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

fastify (7 failed):

  • parallelSleepWorkflow | wrun_01KT53G0MBRA3HPSFV00DTSMGZ | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

hono (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nextjs-turbopack (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nextjs-webpack (7 failed):

  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_01KT53YR1QQX6YH41XRK41T24S | 🔍 observability
  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nitro (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

sveltekit (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

vite (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
📋 Other (6 failed)

e2e-vercel-prod-tanstack-start (6 failed):

  • experimental_setAttributes start: initial attributes are seeded on run creation
  • experimental_setAttributes experimentalSetAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • experimental_setAttributes experimentalSetAttributesInsideStepWorkflow: step-body calls append attributed native events
  • experimental_setAttributes fire-and-forget: void experimental_setAttributes lands without awaiting
  • experimental_setAttributes Promise.all of disjoint-key writes: every key lands
  • experimental_setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

Details by Category

❌ ▲ Vercel Production
App Passed Failed Skipped
❌ astro 105 6 26
❌ example 105 6 26
❌ express 105 6 26
❌ fastify 104 7 26
❌ hono 105 6 26
❌ nextjs-turbopack 129 6 2
❌ nextjs-webpack 128 7 2
❌ nitro 105 6 26
❌ nuxt 105 6 26
❌ sveltekit 124 6 7
❌ vite 105 6 26
✅ 💻 Local Development
App Passed Failed Skipped
✅ astro-stable 112 0 25
✅ express-stable 112 0 25
✅ fastify-stable 112 0 25
✅ hono-stable 112 0 25
✅ nextjs-turbopack-canary 118 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 137 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 137 0 0
✅ nextjs-webpack-canary 118 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 137 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 137 0 0
✅ nitro-stable 112 0 25
✅ nuxt-stable 112 0 25
✅ sveltekit-stable 131 0 6
✅ vite-stable 112 0 25
✅ 📦 Local Production
App Passed Failed Skipped
✅ astro-stable 112 0 25
✅ express-stable 112 0 25
✅ fastify-stable 112 0 25
✅ hono-stable 112 0 25
✅ nextjs-turbopack-canary 118 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 137 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 137 0 0
✅ nextjs-webpack-canary 118 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 137 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 137 0 0
✅ nitro-stable 112 0 25
✅ nuxt-stable 112 0 25
✅ sveltekit-stable 131 0 6
✅ vite-stable 112 0 25
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ astro-stable 112 0 25
✅ express-stable 112 0 25
✅ fastify-stable 112 0 25
✅ hono-stable 112 0 25
✅ nextjs-turbopack-canary 118 0 19
✅ nextjs-turbopack-stable-lazy-discovery-disabled 137 0 0
✅ nextjs-turbopack-stable-lazy-discovery-enabled 137 0 0
✅ nextjs-webpack-canary 118 0 19
✅ nextjs-webpack-stable-lazy-discovery-disabled 137 0 0
✅ nextjs-webpack-stable-lazy-discovery-enabled 137 0 0
✅ nitro-stable 112 0 25
✅ nuxt-stable 112 0 25
✅ sveltekit-stable 131 0 6
✅ vite-stable 112 0 25
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 137 0 0
❌ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 112 0 25
✅ e2e-local-dev-tanstack-start- 112 0 25
✅ e2e-local-postgres-nest-stable 112 0 25
✅ e2e-local-postgres-tanstack-start- 112 0 25
✅ e2e-local-prod-nest-stable 112 0 25
✅ e2e-local-prod-tanstack-start- 112 0 25
❌ e2e-vercel-prod-tanstack-start 105 6 26

📋 View full workflow run


Some E2E test jobs failed:

  • Vercel Prod: failure
  • Local Dev: success
  • Local Prod: success
  • Local Postgres: success
  • Windows: success

Check the workflow run for details.

Copy link
Copy Markdown
Contributor

@karthikscale3 karthikscale3 left a comment

Choose a reason for hiding this comment

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

Reviewed the SDK + world changes. Nicely tested across all three worlds, and the step-vs-workflow dedup is consistent end-to-end (NULL correlationId keeps step writes repeatable; workflow writes are correlated one-shots, matching the DynamoDB guard). I also confirmed the attribute events are awaited via await Promise.all(ops) before the suspension returns, so the immediate re-invoke on hasAttributeEvents isn't racy. A few inline notes — none blocking.

? { allowReservedAttributes: true }
: {}
);
await world.events.create(runId, {
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.

start() got an explicit specVersion < SPEC_VERSION_SUPPORTS_ATTRIBUTES throw with a clear message, but the step-body (and workflow-body) experimental_setAttributes paths call world.events.create({ eventType: 'attr_set' }) with no spec-version check. On a pre-v4 world this surfaces as a raw world/server rejection rather than the friendly "requires spec version 4" error. Looks intentional given the docs now state attributes require v4 and errors should surface rather than no-op — just flagging the asymmetry in error quality vs. start().

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, this is intentional for the v4-only feature contract. start() rejects before creating a run because it can determine capability up front; workflow/step body writes emit the native v4 attr_set event and let an unsupported World reject it. We explicitly do not want a v3 fallback for this rollout.


// Native workflow attribute events are resolved through
// replay; re-invoke now that their events are durable.
if (suspensionResult.hasAttributeEvents) {
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.

This mirrors the hasHookConflict early-return, but note it preempts the pendingSteps handling below: a suspension batch containing both an attribute write and pending steps will re-invoke immediately (timeoutSeconds: 0) and defer step handling to the next replay. Hook conflicts are rare so that precedent is cheap; attribute writes commonly co-occur with steps, so this adds an extra re-invoke cycle to those workflows. Is deferring steps here intended, or should attribute-driven re-invoke fold into the existing step/timeout path instead of short-circuiting ahead of it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Deferring is intentional: with Promise.race([experimental_setAttributes(...), slowStep()]), the persisted attribute event can win on replay without executing the losing step. Processing pendingSteps first would introduce a side effect that the deterministic replay says lost. I documented that decision in runtime.ts and added replays attribute events before executing a step that loses the same race in runtime.test.ts in 17ccd34.

}),
z.object({
type: z.literal('step'),
stepId: z.string(),
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.

Nit: the client writer schema uses stepId: z.string() / attempt: z.number(), while the server validates StepId.schema + attempt: z.number().int().min(0). The server is authoritative so this is safe, just an asymmetry — the client would accept a non-int/negative attempt that the server then rejects.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed that the server is stricter here. @workflow/world already models the existing public step event/payload surfaces with generic string IDs and number attempts (StepStartedEventSchema and queue payloads), while the server remains authoritative for platform ID and attempt constraints. I am keeping attr_set consistent with that client-side contract rather than tightening only this event.

runInputData.workflowName &&
runInputData.input !== undefined
) {
validateAttributeChanges(
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.

Might allow SQL injection since we don't validate since validateAttributeChanges doesn't do special character checks , though maybe sql has some built in defenses there, probably good to sanitize more strictly

'@workflow/world-postgres': minor
---

Add native v4 attribute events and seed run attributes through `start()`.
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.

Suggested change
Add native v4 attribute events and seed run attributes through `start()`.
Add native event-based attribute setting and allow passing initial run attributes through `start()`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants