Skip to content

fix(swc-plugin): remove __builtin special-case step ID generation#1607

Merged
VaguelySerious merged 1 commit into
pgp/revert-revert-startfrom
peter/builtin-step-id-registration
Apr 3, 2026
Merged

fix(swc-plugin): remove __builtin special-case step ID generation#1607
VaguelySerious merged 1 commit into
pgp/revert-revert-startfrom
peter/builtin-step-id-registration

Conversation

@VaguelySerious
Copy link
Copy Markdown
Member

Summary

  • Removes a hardcoded special case in the SWC Rust plugin that generated bare function names (no step// prefix, no module specifier) for any function whose name starts with __builtin
  • With this fix, __builtin_response_* functions get proper step//workflow/internal/builtins@{version}//{name} IDs, matching what builtinStepId() constructs in the workflow VM

Context

The __builtin special case (at transform/src/lib.rs:1581) was added so the workflow VM could look up response builtins by bare name. Since #1491 switches the VM to fully-qualified IDs via builtinStepId(), the special case causes a mismatch:

Registered step ID VM lookup ID
Before __builtin_response_text __builtin_response_text
After #1491 __builtin_response_text step//workflow/internal/builtins@4.2.0-beta.76//__builtin_response_text
After this fix step//workflow/internal/builtins@4.2.0-beta.76//__builtin_response_text step//workflow/internal/builtins@4.2.0-beta.76//__builtin_response_text

The parent commit (Fix 1) adds a fallback alias in getStepFunction() that unblocks the tests even without this fix, but this fix makes the IDs match directly — which is the architecturally correct solution.

Test plan

  • 138 Rust tests pass (cargo test in transform crate)
  • 573 @workflow/core tests pass
  • 124 @workflow/builders tests pass
  • Verified registerStepFunction output in example workbench build — all 12 builtins now have proper step//workflow/internal/builtins@{version}//... IDs

🤖 Generated with Claude Code

The SWC plugin had a hardcoded special case that generated bare function
names (no step// prefix, no module specifier) for any function whose name
starts with "__builtin". This was originally intended to produce stable,
version-independent IDs for the workflow VM.

With the introduction of builtinStepId() in @workflow/core, the VM now
constructs fully-qualified step IDs (step//workflow/internal/builtins@{version}//name).
The bare-name special case causes a mismatch: the VM requests a qualified ID
but only a bare name is registered, causing StepNotRegisteredError.

Remove the special case so __builtin functions follow the standard step ID
generation path, receiving proper step//moduleSpecifier//name IDs like all
other step functions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@VaguelySerious VaguelySerious requested a review from a team as a code owner April 3, 2026 21:06
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 3, 2026

⚠️ No Changeset found

Latest commit: 3e51bd7

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 3, 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 Apr 3, 2026 9:09pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Apr 3, 2026 9:09pm
example-workflow Ready Ready Preview, Comment Apr 3, 2026 9:09pm
workbench-astro-workflow Ready Ready Preview, Comment Apr 3, 2026 9:09pm
workbench-express-workflow Ready Ready Preview, Comment Apr 3, 2026 9:09pm
workbench-fastify-workflow Ready Ready Preview, Comment Apr 3, 2026 9:09pm
workbench-hono-workflow Ready Ready Preview, Comment Apr 3, 2026 9:09pm
workbench-nitro-workflow Ready Ready Preview, Comment Apr 3, 2026 9:09pm
workbench-nuxt-workflow Ready Ready Preview, Comment Apr 3, 2026 9:09pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Apr 3, 2026 9:09pm
workbench-vite-workflow Ready Ready Preview, Comment Apr 3, 2026 9:09pm
workflow-docs Ready Ready Preview, Comment, Open in v0 Apr 3, 2026 9:09pm
workflow-swc-playground Ready Ready Preview, Comment Apr 3, 2026 9:09pm

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 3, 2026

🧪 E2E Test Results

Some tests failed

Summary

Passed Failed Skipped Total
✅ ▲ Vercel Production 800 0 60 860
✅ 💻 Local Development 710 0 150 860
✅ 📦 Local Production 710 0 150 860
✅ 🐘 Local Postgres 710 0 150 860
✅ 🪟 Windows 78 0 8 86
❌ 🌍 Community Worlds 138 63 24 225
✅ 📋 Other 216 0 42 258
Total 3362 63 584 4009

❌ Failed Tests

🌍 Community Worlds (63 failed)

mongodb (3 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNAJWDD2PWGT17JAQGRJRPPZ
  • webhookWorkflow | wrun_01KNAJWNVKDGV95787T826DAST
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNAK4481THVEEM1NTVB143RZ

redis (2 failed):

  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNAJWDD2PWGT17JAQGRJRPPZ
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNAK4481THVEEM1NTVB143RZ

turso (58 failed):

  • addTenWorkflow | wrun_01KNAJVAY23C7ZZ4FDHYHES2Q4
  • addTenWorkflow | wrun_01KNAJVAY23C7ZZ4FDHYHES2Q4
  • wellKnownAgentWorkflow (.well-known/agent) | wrun_01KNAJX9N6M8XW52G1BKB1TJRD
  • should work with react rendering in step
  • promiseAllWorkflow | wrun_01KNAJVGX0NAX28RMDJB72J30P
  • promiseRaceWorkflow | wrun_01KNAJVMZ9B3FF50WKX1R6XE24
  • promiseAnyWorkflow | wrun_01KNAJVPX7HKWV58F6QPFSB8JN
  • importedStepOnlyWorkflow | wrun_01KNAJXNJFP2HTSFF1BC8982YA
  • hookWorkflow | wrun_01KNAJW30A9CA5GFMXYEDW93H8
  • hookWorkflow is not resumable via public webhook endpoint | wrun_01KNAJWDD2PWGT17JAQGRJRPPZ
  • webhookWorkflow | wrun_01KNAJWNVKDGV95787T826DAST
  • sleepingWorkflow | wrun_01KNAJWVTH6WNHX27VSPP5CNXX
  • parallelSleepWorkflow | wrun_01KNAJX7MDGJD87MKYMNW37DFW
  • nullByteWorkflow | wrun_01KNAJXBPNRJTJJFKXBQXPK0KN
  • workflowAndStepMetadataWorkflow | wrun_01KNAJXFSYM1S2BEK0JTGJAQZW
  • fetchWorkflow | wrun_01KNAK03VD6RGT0V4D7593106T
  • promiseRaceStressTestWorkflow | wrun_01KNAK07ZFA1XPG1R8Z5XKTCJJ
  • error handling error propagation workflow errors nested function calls preserve message and stack trace
  • error handling error propagation workflow errors cross-file imports preserve message and stack trace
  • error handling error propagation step errors basic step error preserves message and stack trace
  • error handling error propagation step errors cross-file step error preserves message and function names in stack
  • error handling retry behavior regular Error retries until success
  • error handling retry behavior FatalError fails immediately without retries
  • error handling retry behavior RetryableError respects custom retryAfter delay
  • error handling retry behavior maxRetries=0 disables retries
  • error handling catchability FatalError can be caught and detected with FatalError.is()
  • error handling not registered WorkflowNotRegisteredError fails the run when workflow does not exist
  • error handling not registered StepNotRegisteredError fails the step but workflow can catch it
  • error handling not registered StepNotRegisteredError fails the run when not caught in workflow
  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_01KNAK3FEPR1B4FF8TV80BEQG3
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_01KNAK4481THVEEM1NTVB143RZ
  • hookDisposeTestWorkflow - hook token reuse after explicit disposal while workflow still running | wrun_01KNAK4SRYKR59M8TE6YJT0Z52
  • stepFunctionPassingWorkflow - step function references can be passed as arguments (without closure vars) | wrun_01KNAK5D2EVXSZMJBN4KWM66JE
  • stepFunctionWithClosureWorkflow - step function with closure variables passed as argument | wrun_01KNAK5PSWAH8GT34DEYSP4FR1
  • closureVariableWorkflow - nested step functions with closure variables | wrun_01KNAK5WMMYERWY3W03JCWV503
  • spawnWorkflowFromStepWorkflow - spawning a child workflow using start() inside a step | wrun_01KNAK5YJX8P2QQKH7AQFWB008
  • startFromWorkflow - calling start() directly inside a workflow function with hook communication | wrun_01KNAK69JBXHBB5Q519JY5W4BS
  • fibonacciWorkflow - recursive workflow composition via start() | wrun_01KNAK6BY33267E48BH849281Z
  • health check (queue-based) - workflow and step endpoints respond to health check messages
  • pathsAliasWorkflow - TypeScript path aliases resolve correctly | wrun_01KNAK6SS75MS5A3XZ7AJFTRPD
  • Calculator.calculate - static workflow method using static step methods from another class | wrun_01KNAK6YQYC173Z1N7PWXK1VAY
  • AllInOneService.processNumber - static workflow method using sibling static step methods | wrun_01KNAK75S2WECEG8DFER300YKJ
  • ChainableService.processWithThis - static step methods using this to reference the class | wrun_01KNAK7CQ708EA1EF7Y17YVXV2
  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_01KNAK7JP0ZNH4H3WPD29GQQWC
  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_01KNAK7TC7D3NBPVEW1EEVPC3A
  • instanceMethodStepWorkflow - instance methods with "use step" directive | wrun_01KNAK83J8Z1FAKNVX5H4FTQT5
  • crossContextSerdeWorkflow - classes defined in step code are deserializable in workflow context | wrun_01KNAK8DMZGMDNH79VK9ET4H67
  • stepFunctionAsStartArgWorkflow - step function reference passed as start() argument | wrun_01KNAK8MNBW7QRFH91J14FYJPX
  • cancelRun - cancelling a running workflow | wrun_01KNAK8THW2GRZHBC5CSHRRK42
  • cancelRun via CLI - cancelling a running workflow | wrun_01KNAK9342XH5GYKKB0Y3GW485
  • pages router addTenWorkflow via pages router
  • pages router promiseAllWorkflow via pages router
  • pages router sleepingWorkflow via pages router
  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_01KNAK9E6WA288RED8PVR2HWEV
  • sleepInLoopWorkflow - sleep inside loop with steps actually delays each iteration | wrun_01KNAKA0V7M1HAMW5YB75WN2G6
  • sleepWithSequentialStepsWorkflow - sequential steps work with concurrent sleep (control) | wrun_01KNAKABP8DSFXRP17WVE1RKF9
  • importMetaUrlWorkflow - import.meta.url is available in step bundles | wrun_01KNAKAJ633AEGTA8WEDKD4NA5
  • metadataFromHelperWorkflow - getWorkflowMetadata/getStepMetadata work from module-level helper (#1577)

Details by Category

✅ ▲ Vercel Production
App Passed Failed Skipped
✅ astro 79 0 7
✅ example 79 0 7
✅ express 79 0 7
✅ fastify 79 0 7
✅ hono 79 0 7
✅ nextjs-turbopack 84 0 2
✅ nextjs-webpack 84 0 2
✅ nitro 79 0 7
✅ nuxt 79 0 7
✅ vite 79 0 7
✅ 💻 Local Development
App Passed Failed Skipped
✅ express-stable 72 0 14
✅ fastify-stable 72 0 14
✅ hono-stable 72 0 14
✅ nextjs-turbopack-canary 61 0 25
✅ nextjs-turbopack-stable 78 0 8
✅ nextjs-webpack-canary 61 0 25
✅ nextjs-webpack-stable 78 0 8
✅ nitro-stable 72 0 14
✅ nuxt-stable 72 0 14
✅ vite-stable 72 0 14
✅ 📦 Local Production
App Passed Failed Skipped
✅ express-stable 72 0 14
✅ fastify-stable 72 0 14
✅ hono-stable 72 0 14
✅ nextjs-turbopack-canary 61 0 25
✅ nextjs-turbopack-stable 78 0 8
✅ nextjs-webpack-canary 61 0 25
✅ nextjs-webpack-stable 78 0 8
✅ nitro-stable 72 0 14
✅ nuxt-stable 72 0 14
✅ vite-stable 72 0 14
✅ 🐘 Local Postgres
App Passed Failed Skipped
✅ express-stable 72 0 14
✅ fastify-stable 72 0 14
✅ hono-stable 72 0 14
✅ nextjs-turbopack-canary 61 0 25
✅ nextjs-turbopack-stable 78 0 8
✅ nextjs-webpack-canary 61 0 25
✅ nextjs-webpack-stable 78 0 8
✅ nitro-stable 72 0 14
✅ nuxt-stable 72 0 14
✅ vite-stable 72 0 14
✅ 🪟 Windows
App Passed Failed Skipped
✅ nextjs-turbopack 78 0 8
❌ 🌍 Community Worlds
App Passed Failed Skipped
✅ mongodb-dev 5 0 0
❌ mongodb 59 3 8
✅ redis-dev 5 0 0
❌ redis 60 2 8
✅ turso-dev 5 0 0
❌ turso 4 58 8
✅ 📋 Other
App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 72 0 14
✅ e2e-local-postgres-nest-stable 72 0 14
✅ e2e-local-prod-nest-stable 72 0 14

📋 View full workflow run

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.

AI Review:

Assessment: Low risk, architecturally correct — no blockers

What this does

Deletes 7 lines from the SWC Rust plugin's create_id function — a special case that generated bare function names (e.g. __builtin_response_text) for any function starting with __builtin, instead of the standard step//{moduleSpecifier}//{name} format.

Why it's needed

The base branch (pgp/revert-revert-start) introduces builtinStepId() in packages/core/src/workflow/builtin-step-id.ts, which changes the VM to look up builtins using fully-qualified IDs:

// VM now requests:
step//workflow/internal/builtins@{version}//__builtin_response_text

Without this PR, the SWC plugin still registers the bare name __builtin_response_text, creating a mismatch.

No regressions expected

  1. Safety net exists: The base branch already adds getBuiltinStepAlias() which handles both directions — bare-name lookup of qualified registrations AND qualified lookup of bare-name registrations. Even without this PR the system works; this PR makes the direct match path work, eliminating the need for the fallback.

  2. No event replay concerns: The workflow VM uses a seeded PRNG (vmGlobalThis.Math.random()) for deterministic ULID generation, so correlationIds match during replay. The stepName is only used for step function dispatch, not event matching. And dispatch is guarded by the getBuiltinStepAlias fallback.

  3. No user-facing __builtin convention: The __builtin prefix is internal SDK convention. User code should never use it.

  4. Spec-aligned: spec.md on the base branch already documents builtins with qualified IDs (step//workflow/internal/builtins@4.0.0//start), so this PR aligns the implementation with the spec.

Minor callout

Missing changeset: The changeset-bot flagged this. Since this changes SWC plugin behavior (step ID generation for __builtin functions), it should include a patch changeset for @workflow/swc-plugin before merge.

@VaguelySerious VaguelySerious merged commit b394412 into pgp/revert-revert-start Apr 3, 2026
78 of 85 checks passed
@VaguelySerious VaguelySerious deleted the peter/builtin-step-id-registration branch April 3, 2026 21:27
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.

2 participants