migrating-to-workflow-sdk
Migrates Temporal, Inngest, Trigger.dev, and AWS Step Functions workflows to the Workflow SDK. Use when porting Activities, Workers, Signals, step.run(), step.waitForEvent(), Trigger.dev tasks / wait.forToken / triggerAndWait, ASL JSON state machines, Task/Choice/Wait/Parallel states, task tokens, o
By vercel · 521 installs
npx skills add vercel/workflow --skill migrating-to-workflow-sdk
Source repository · Upstream listing
Migrating to the Workflow SDK
Use this skill when converting an existing orchestration system to the Workflow SDK.
Intake
1. Identify the source system:
Temporal
Inngest
Trigger.dev
AWS Step Functions
2. Identify the target runtime:
Managed hosting keep examples focused on start() , getRun() , hooks/webhooks, and route handlers.
Self hosted also read references/runtime targets.md and explicitly say the workflow/step code can stay the same, but deployment still needs a World implementation and startup bootstrap.
3. Extract the source constructs:
entrypoint
waits / timers
external callbacks / approvals
retries / failure handling
child workflows / fan out
progress streaming
external side effects
Default migration rules
Put orchestration in "use workflow" functions.
Put side effects, SDK calls, DB calls, HTTP calls, and stream I/O in "use step" functions.
Use sleep() only in workflow context.
For Signals, step.waitForEvent() , and .waitForTaskToken , choose exactly one resume surface:
resume/internal createHook() + resumeHook() when the app resumes from server side code with a deterministic business token.
resume/url/default createWebhook() when the external system needs a generated callback URL and the default 202 Accepted response is fine.
resume/url/manual createWebhook({ respondWith: 'manual' }) only when the prompt explicitly requires a custom response body, status, or headers.
If a callback URL prompt does not specify response semantics, default to resume/url/default and make the assumption explicit in Open Questions .
Never pair createWebhook() with resumeHook() , and never pass token: to createWebhook() .
Wrap start() and getRun() inside "use step" functions for child runs.
Use getStepMetadata().stepId as the idempotency key for external writes.
Use getWritable() in workflow context to obtain the stream, but interact with it (write, close) only inside "use step" functions.
Prefer rollback stacks for multi step compensation.
Choose app boundary syntax in this order:
1. If the prompt explicitly asks for framework agnostic app boundary code, use plain Request / Response even when a framework like Hono is named.
2. Otherwise, if the target framework is named, shape app boundary examples to that framework.
3. Otherwise, keep examples framework agnostic with Request / Response . Do not default to Next.js only route signatures unless Next.js is explicitly named.
Fast memory aid:
Callback URL + default ack createWebhook()
Callback URL + custom ack createWebhook({ respondWith: 'manual' })
Deterministic server side resume createHook() + resumeHook()
Fast path router
Load references/resume routing.md when the source pauses for Signals, step.waitForEvent() , or .waitForTaskToken .
Fast defaults:
callback URL only resume/url/default
callback URL + explicit custom response resume/url/manual
deterministic server side resume resume/internal
self hosted add runtime/self hosted
named framework add boundary/named framework
explicit framework agnostic request add boundary/framework agnostic
Before drafting Migrated Code , write the selected route keys in Migration Plan .
Source references
Temporal references/temporal.md
Inngest references/inngest.md
Trigger.dev references/trigger dev.md
AWS Step Functions references/aws step functions.md
Shared references
references/shared patterns.md : Reusable code templates for hooks, child workflows, idempotency, streaming, and rollback.
references/runtime targets.md : Managed vs. custom World guidance.
references/resume routing.md : Route key selection, obligations, and exact Migration Plan shape.
references/retries.md : Canonical retry mechanics, including stepFn.maxRetries , RetryableError({ retryAfter }) , and FatalError .
Required output shape
Return the migration in this structure:
Verification checklist
Fail the draft if any of these are true:
[ ] Migration Plan omits Route keys
[ ] Migration Plan omits Why these route keys
[ ] Migration Plan lists route keys that do not match the prompt
[ ] Migration Plan lists required code obligations that do not match the selected route keys
[ ] Source framework primitives remain in the migrated code
[ ] Side effects remain in workflow context
[ ] sleep() appears inside a step
[ ] Stream interaction ( getWriter() , write() , close() ) appears inside a workflow function
[ ] Child workflows call start() / getRun() directly from workflow context
[ ] External writes omit idempotency keys
[ ] Hooks/webhooks are missing where the source used signals, waitForEvent, or task tokens
[ ] A callback URL flow uses createHook() + resumeHook() instead of createWebhook()
[ ] A resume/url/default or resume/url/manual migration invents a user authored callback route or resumeWebhook() wrapper when webhook.url should be the only resume surface
[ ] createWebhook() is given a custom token or paired with resumeHook()
Validation note:
Reading webhook request data in workflow context is allowed. Only request.respondWith() is step only.
Additional fail conditions:
resume/internal output omits resumeHook() in app boundary code
resume/internal output omits a deterministic business token
resume/internal output emits createWebhook() or webhook.url
resume/url/default output does not pass webhook.url to the external system
resume/url/default output emits resumeHook() , respondWith: 'manual' , or RequestWithResponse without a custom response requirement in the prompt
resume/url/default output invents a user authored callback route or resumeWebhook() wrapper when webhook.url is the intended resume surface
resume/url/manual output does not pass webhook.url to the external system
resume/url/manual output omits RequestWithResponse or await request.respondWith(...)
resume/url/manual output calls request.respondWith(...) outside a "use step" function
resume/url/manual output invents a user authored callback route or resumeWebhook() wrapper when webhook.url is the intended resume surface
createWebhook() is paired with resumeHook()
self hosted output omits World extends Queue, Streamer, Storage , startWorkflowWorld() , or the explicit note that the workflow and step code can stay the same while the app still needs a custom World
named framework output mixes framework syntax with plain Request / Response app boundary code without a framework agnostic override
For concrete passing code, load:
references/shared patterns.md Generated callback URL (default response)
references/shared patterns.md Generated callback URL (manual response)
references/runtime targets.md Self hosted output block
references/aws step functions.md Combined recipe: callback URL on self hosted Hono
Sample prompt
Expected response shape:
Example references
Load a worked example only when the prompt needs concrete code:
references/shared patterns.md Named framework internal resume example (Hono)
references/shared patterns.md Generated callback URL (default response)
references/shared patterns.md Generated callback URL (manual response)
references/runtime targets.md Self hosted output block
references/aws step functions.md Combined recipe: callback URL on self hosted Hono
Reject these counterexamples:
resume/url/default or resume/url/manual + user authored callback route when webhook.url is the intended resume surface
createWebhook() paired with resumeHook()
named framework app boundary output mixed with plain Request / Response without a framework agnostic override