SKILL.md
Platformatic Workflow SDK Skill
You are an expert in building applications with the Vercel Workflow SDK (workflow / @workflow/*) against @platformatic/world, the self-hosted World adapter for Platformatic.
This skill helps users build apps that use workflows. It does not cover deploying the Workflow Service — assume one is reachable at the URL the user will supply as PLTWORLDSERVICE_URL.
Prerequisites Check
Before any workflow setup, verify:
- Node.js Version:
@platformatic/worldrequires Node.js v22.19.0+
``bash node --version ``
- Workflow Service URL: The user needs a reachable Workflow Service.
- Local dev: typically http://localhost:3042 (started via npx @platformatic/workflow postgresql://...). - K8s: provided by the Platformatic control plane (ICC) or operator. If the user does not have one, point them at @platformatic/workflow quick start before continuing — but do not try to start the service from this skill.
- Existing package.json: This skill installs into an existing Node app.
Command Router
Based on user input ($ARGUMENTS), route to the appropriate workflow:
| Input Pattern | Action |
|---|---|
init, setup, add, (empty) |
Run Detect Framework + World Setup |
next, nextjs, next.js |
Run Next.js Setup |
node, express, koa, generic |
Run Generic Node.js Setup |
fastify |
Run Fastify Setup (with plugin) |
author, write, use workflow, use step, step, hook, sleep, stream |
Read [references/authoring.md](references/authoring.md) |
trigger, start, run |
Run Trigger a Workflow guidance |
build, standalone |
Run Standalone Build Workflow |
env, config |
Run Environment Configuration |
status, check |
Run Status Check |
troubleshoot, debug |
Read [references/troubleshooting.md](references/troubleshooting.md) |
Detect Framework + World Setup
When the user asks to "add workflow" without specifying a framework:
- Detect the framework by inspecting the project:
- next.config.{js,ts,mjs} or next in package.json → Next.js Setup - fastify in package.json → Fastify Setup (with plugin) (the plugin is optional — also offer plain Node setup) - Any other Node server (Express/Koa/Hono/plain HTTP) → Generic Node.js Setup
- Confirm the detection with the user before generating files.
In all cases, the core install is the same:
npm install workflow @platformatic/world
workflow is the Vercel SDK; @platformatic/world is the World adapter that points the SDK at the Platformatic Workflow Service.
Then read:
- [references/world.md](references/world.md) — full client reference (env
vars, createWorld, world.start(), framework wiring).
- [references/authoring.md](references/authoring.md) — how to write
workflows and steps with the Vercel SDK ('use workflow', 'use step', retries, hooks, sleeps, streams, determinism rules).
Continue with the framework-specific section below for the wiring details.
Next.js Setup
When the user runs 'use workflow' / 'use step' files inside a Next.js app:
- Install:
``bash npm install workflow @platformatic/world ``
- Read [references/world.md](references/world.md) — section "Next.js wiring".
- Create
instrumentation.tsat the project root:
``ts // instrumentation.ts export async function register () { if (process.env.PLTWORLDSERVICE_URL) { const { createWorld } = await import('@platformatic/world') const world = createWorld() await world.start?.() } } ` This registers the app's queue handler endpoints (/.well-known/workflow/v1/{flow,step,webhook}) with the Workflow Service on boot. In K8s + ICC, world.start()` is a no-op (ICC registers handlers via the control plane).
- Set environment variables in
.env.local:
`` WORKFLOWTARGETWORLD=@platformatic/world PLTWORLDSERVICEURL=http://localhost:3042 # Optional: PLTWORLDAPPID=my-app-id # defaults to package.json name PLTWORLDDEPLOYMENT_VERSION=local # auto-detected in K8s ``
- Author workflows the standard Vercel way — the SDK transforms
'use workflow' / 'use step' files automatically inside Next.js.
- Trigger runs from a route handler or server action:
```ts import { start } from 'workflow/api' import { handleSignup } from '@/workflows/signup'
await start(handleSignup, [email]) ```
- Verify with
npm run devand watch the Next.js logs for the
[workflow] handler registration line.
Generic Node.js Setup
For Node servers without a framework-specific transform (Express, Koa, Hono, plain http), the SDK still works but you call world.start() explicitly.
- Install:
``bash npm install workflow @platformatic/world ``
- Read [references/world.md](references/world.md) — section "Manual world.start()".
- Call
world.start()once during server boot, before accepting traffic:
```ts import { createWorld } from '@platformatic/world'
const world = createWorld() await world.start?.() // ...then app.listen(PORT) ```
- Set environment variables (see [references/world.md](references/world.md)).
- The Vercel SDK's transform is required to author
'use workflow'/
'use step' files. If the host framework does not transform them, use the standalone build (see Standalone Build Workflow) and mount the resulting handlers on your routes — or use the Fastify plugin which does this for you.
Fastify Setup (with plugin)
When the user wants to host Vercel Workflow SDK workflows inside a Fastify app, recommend @platformatic/workflow-fastify. It is optional — they can also do the work manually following the Generic Node.js Setup.
- Install:
``bash npm install fastify workflow @platformatic/world @platformatic/workflow-fastify ``
- Read [references/fastify.md](references/fastify.md).
- Author workflow files (e.g.
workflows/signup.ts) with
'use workflow' / 'use step'.
- Build the standalone bundles (see Standalone Build Workflow below):
``bash npx workflow build --target standalone ` This emits flow, step, webhook, and manifest.json under .well-known/workflow/v1/`.
- Register the plugin in your Fastify app:
```ts import Fastify from 'fastify' import { start } from 'workflow/api' import workflowFastify from '@platformatic/workflow-fastify'
const app = Fastify() await app.register(workflowFastify)
app.post('/api/signup', async (req) => { const run = await start( { workflowId: app.workflows.handleSignup }, [req.body.email] ) return { runId: run.runId } })
await app.listen({ port: Number(process.env.PORT) }) ```
- Set environment variables (same set as Next.js / generic Node).
- Run the build before each start (or wire it into your build script):
``bash npx workflow build --target standalone && node server.js ``
The plugin:
- Mounts
flow,step,webhookhandlers under/.well-known/workflow/v1/. - Decorates the Fastify instance with
app.workflows(name → workflowId map). - Calls
world.start()on boot to register the queue handler.
Trigger a Workflow
When the user asks how to start a workflow run from application code:
- Import the trigger API from the Vercel SDK, not from
@platformatic/world:
``ts import { start } from 'workflow/api' ``
- Two ways to identify the workflow:
- By imported reference (works inside files the SDK transforms — typically Next.js routes/actions): ``ts import { handleSignup } from '@/workflows/signup' const run = await start(handleSignup, [email]) ` - By workflowId string (works anywhere — required outside transformed files, e.g. plain Fastify route handlers): `ts const run = await start({ workflowId: app.workflows.handleSignup }, [email]) ``
- To resume a hook (webhook or human-in-the-loop):
``ts import { resumeHook } from 'workflow/api' await resumeHook(token, payload) ``
- Tell the user the trigger call is just an HTTP POST to the Workflow
Service — no special transport is required in the calling process.
Standalone Build Workflow
The Vercel SDK's workflow build --target standalone produces self-contained bundles for hosts that don't have a built-in SDK transform (e.g. Fastify, plain Node, scripts).
- The build reads
'use workflow'/'use step'files from your source tree. - It writes to
<buildDir>/.well-known/workflow/v1/:
- flow — workflow orchestration handler (POST, Web Request => Response) - step — step execution handler - webhook — webhook resume handler - manifest.json — workflow/step ids and graph
- Extensions vary by SDK version: v5 emits
.mjs; v4 emits.js(flow/step
CommonJS, webhook ESM). @platformatic/workflow-fastify handles both transparently.
- Re-run the build whenever workflow source files change. In CI, run it as
a pre-build step before the host app is packaged.
- Don't commit
.well-known/workflow/v1/— add it to.gitignorealongside
dist/.
npx workflow build --target standalone
Inside Next.js you do not need this — the Next plugin transforms workflows inline. The standalone build is for non-Next hosts.
Environment Configuration
The SDK and @platformatic/world read these environment variables:
| Variable | Required | Default | Description |
|---|---|---|---|
WORKFLOWTARGETWORLD |
Yes | — | Set to @platformatic/world to select this adapter |
PLTWORLDSERVICE_URL |
Yes | — | Workflow Service base URL (e.g. http://localhost:3042) |
PLTWORLDAPP_ID |
No | package.json name |
Application identifier (multi-tenant routing) |
PLTWORLDDEPLOYMENT_VERSION |
No | K8s label or 'local' |
Version each run pins to |
PORT |
No | — | Used by world.start() to register the callback URL locally |
In K8s, PLTWORLDDEPLOYMENT_VERSION is auto-detected from the pod's plt.dev/version label via the K8s API — usually leave it unset there.
For local development without K8s, the Workflow Service runs in single-tenant mode and any appId is accepted.
Status Check
When user runs /workflow status:
- Node.js Version
``bash node --version `` Check if >= v22.19.0.
- Dependencies present
``bash node -e "require('workflow/package.json')" node -e "require('@platformatic/world/package.json')" ``
- Required env vars set
``bash node -e "console.log({world:process.env.WORKFLOWTARGETWORLD, url:process.env.PLTWORLDSERVICEURL})" ` WORKFLOWTARGETWORLD must equal @platformatic/world. PLTWORLDSERVICEURL` must be a reachable URL.
- Service reachable
``bash curl -fsS "$PLTWORLDSERVICE_URL/api/v1/apps/default/runs" >/dev/null && echo OK ``
- For Next.js:
instrumentation.tsexists at the project root and
calls world.start() inside register().
- For Fastify with plugin:
.well-known/workflow/v1/manifest.jsonexists
(i.e. the standalone build has been run).
Report findings in a clear format:
Workflow SDK Configuration Status
=================================
Node.js Version: vX.X.X [OK/UPGRADE NEEDED]
workflow installed: [vX.X.X/Missing]
@platformatic/world: [vX.X.X/Missing]
WORKFLOW_TARGET_WORLD: [@platformatic/world/Missing]
PLT_WORLD_SERVICE_URL: [URL/Missing]
Service reachable: [OK/Unreachable]
Framework wiring: [instrumentation.ts/Fastify plugin/manual]
Standalone build: [Present/Not built/N/A]
Important Notes
@platformatic/worldis a runtime drop-in for@workflow/world. The
Vercel SDK calls into it via the standard World interface; nothing application-level changes.
- **
world.start()must run exactly once per process before accepting
traffic.** Calling it twice is harmless (idempotent) but wastes a call. Skipping it means queue messages will never reach this process and runs will hang in pending.
- Under K8s + ICC,
world.start()is a no-op — ICC registers handlers
centrally via the control plane.
- Spec version:
@platformatic/worlddeclares spec v3 (CBOR queue
transport). It interoperates with v2 servers and v2 clients, so version skew during rollout is safe.
- SDK compatibility: works at runtime against both
[email protected]
(stable) and [email protected] (next). The world is typed against @workflow/[email protected] but exposes the v5 streams.* namespace at runtime alongside the v4 flat methods.
Troubleshooting
For common issues (runs stuck in pending, world.start() failing, missing callback URL, K8s deployment version mismatch), read [references/troubleshooting.md](references/troubleshooting.md).