/migrating-world-v4-to-v5
Upgrades a custom Workflow SDK World implementation from the v4 spec, or from a 5.0.0 beta, to the current v5 spec (spec version 8). Use when a package implements the `World` interface from `@workflow/world` and is moving to 5.x, or is on a 5.0.0 beta and the runtime reports
Claude Code
$ npx -y skills add vercel/workflow --skill migrating-world-v4-to-v5 --agent claude-code
Codex
$ npx skills add vercel/workflow --skill migrating-world-v4-to-v5 -a codex -y
Cursor
$ npx skills add vercel/workflow --skill migrating-world-v4-to-v5 -a cursor -y
Gemini CLI
$ npx skills add vercel/workflow --skill migrating-world-v4-to-v5 -a gemini-cli -y
GitHub Copilot
$ npx skills add vercel/workflow --skill migrating-world-v4-to-v5 -a github-copilot -y
OpenCode
$ npx skills add vercel/workflow --skill migrating-world-v4-to-v5 -a opencode -y
Windsurf
$ npx skills add vercel/workflow --skill migrating-world-v4-to-v5 -a windsurf -y
SKILL.md
migrating-world-v4-to-v5.SKILL.mdname: migrating-world-v4-to-v5
description: >-
Upgrades a custom Workflow SDK World implementation from the v4 spec, or from a 5.0.0 beta, to the current v5 spec (spec version 8). Use when a package implements the `World` interface from `@workflow/world` and is moving to 5.x, or is on a 5.0.0 beta and the runtime reports `This Workflow runtime supports Worlds with spec version 6 through 8, but the configured World declares spec version N`; or when it shows event IDs that are ULIDs rather than slot positions, `Event id is not slot-numbered` at replay time, a `specVersion` the runtime refuses, `writeToStream` / `closeStream` / `readFromStream` as top-level World methods, `steps.get` or `events.listByCorrelationId` without a `runId`, a `'step'` queue kind or `__wkf_step_*` topics, a `preconditionGuard` capability, or a `createLocalWorld` / `createVercelWorld` factory.
metadata:
author: Vercel Inc.
version: '0.4.0'
Migrating a World from the v4 spec to v5
This skill is for a package that implements `World` from `@workflow/world`: a storage, queue and stream backend the Workflow runtime talks to. It is not for application code. If the task is bumping an app's `workflow` dependency, use the `migrating-workflow-v4-to-v5` skill instead; if the app both uses Workflow and ships its own World, run that skill first and this one second.
An app on the Vercel, Local or Postgres World needs nothing from this skill. Those ship with the SDK and are already on the v5 spec.
One change dominates the work. **Event ID allocation is required, is not visible from the type signatures, and a World that skips it type-checks, starts runs, and fails on the first replay.** Do that part first, then the mechanical rewrites. Do not begin with the type errors: they are the small half, and finishing them produces a World that looks migrated and is not.
Which spec version a pin produces
What a World declares follows the `@workflow/world` version it resolves, and the runtime admits a range. Check both before deciding how much work this is.
| `@workflow/world` | `SPEC_VERSION_CURRENT` / `mintedSpecVersion()` | Admitted by `workflow` 5.2.x (range 6 to 8)? | | --- | --- | --- | | any 4.x | 5 or below | No | | `5.0.0-beta.25` and earlier | 5 or below (no slot identity) | No | | `5.0.0-beta.35` to `beta.37` | 7 | Yes, but not spec 8 | | `5.0.0-beta.39` and later, all 5.0.x stable | 8 | Yes |
The range is printed by the startup error, and lives in the bundled `@workflow/core` as `assertWorldSupportsRuntimeProtocol`. A host that bundles its own copy of the runtime has its own ceiling: grep the bundle for `supports Worlds with spec version` to read it. Declaring above a host's ceiling makes that host refuse the World at startup, so a World that must also run under older hosts states which host versions each of its releases supports.
**A World already on a 5.0.0 beta** with slot-numbered IDs does not redo step 1. Bump `@workflow/world` and `@workflow/errors` to the stable line, confirm the declaration is `mintedSpecVersion()` (step 2), read the spec 8 notes in step 2, then audit the step 5 contract items and the fail checklist: several of them landed late in the beta line and a beta-era World commonly misses lazy `step_started`, the `runs.list` status array and `resolveData: 'skip-step-inputs'`. Run the conformance suite at the new version before calling it done.
**A World that predates event sourcing** (it exposes `runs.create`, `steps.update` and the like, and has no `events.create` that drives entity state) has no v4 event log to port. The whole `events.create` state machine is written from scratch. Use `packages/world-postgres/src/storage.ts` as the reference and keep its event-by-event structure.
Intake
Before editing, establish and report each of these:
1. **Where the World is.** Grep for `implements World`, `: World`, `World>` and `from '@workflow/world'`. Read the factory it exports. 2. **How event IDs are minted today.** Grep for `eventId`, `ulid`, `uuid`, `nanoid`, `nextval`, `AUTO_INCREMENT`, `IDENTITY`. Find the exact line that produces the ID written to storage. Note any place an ID is chosen before the write that stores it, for example an ID planned in advance and saved in a claim record so redeliveries reuse it. That design hands out a position before the commit and needs reworking, not porting. 3. **What settles a write race.** Read the `events.create` implementation. Note whether the ID or ordering is decided in process (read-then-write, an in-memory counter, a `Math.max` over loaded events) or in the store (unique constraint, conditional write, `INSERT ... ON CONFLICT`, a transaction). 4. **Which `specVersion` it declares.** Grep for `specVersion`. Note whether it is a literal or an imported constant. 5. **Which optional members exist.** Grep for `capabilities`, `analytics`, `getRuntimeDeadline`, `getEnvironment`, `createRunId`, `describeRun`, `getEncryptionKeyForRun`, `resolveLatestDeploymentId`, `cancelMany`, `experimentalSetAttributes`. 6. **Whether it provisions step topics.** Grep for `'step'`, `__wkf_step`, `stepQueue`. 7. **Whether it rejects stale writes.** Grep for `PreconditionFailedError`, `preconditionGuard`, `stateUpdatedAt`, `stateEventCount`, `stateCursor`, `412`. 8. **Where its process-wide state lives.** Grep the World's modules for top-level `const`/`let` holding a pool, client, socket, registry, cache, ULID factory, or a log-once boolean. Note every one: these are correct in a `require()`d package and wrong in a bundled one. 9. **How it is tested.** Grep for `@workflow/world-testing` and `createTestSuite`. A World without the conformance suite wired up gets it in this migration. 10. **What its store can make atomic.** A transaction, a conditional write or unique constraint, a server-side script (Redis Lua, a Durable Object or actor that is single-threaded per run), or none (a CRDT or eventually consistent store). Step 1 depends on the answer. 11. **Whether bytes
Read more
name: migrating-world-v4-to-v5 description: >- Upgrades a custom Workflow SDK World implementation from the v4 spec, or from a 5.0.0 beta, to the current v5 spec (spec version 8). Use when a package implements the `World` interface from `@workflow/world` and is moving to 5.x, or is on a 5.0.0 beta and the runtime reports `This Workflow runtime supports Worlds with spec version 6 through 8, but the configured World declares spec version N`; or when it shows event IDs that are ULIDs rather than slot positions, `Event id is not slot-numbered` at replay time, a `specVersion` the runtime refuses, `writeToStream` / `closeStream` / `readFromStream` as top-level World methods, `steps.get` or `events.listByCorrelationId` without a `runId`, a `'step'` queue kind or `__wkf_step_*` topics, a `preconditionGuard` capability, or a `createLocalWorld` / `createVercelWorld` factory. metadata: author: Vercel Inc. version: '0.4.0'
Migrating a World from the v4 spec to v5
This skill is for a package that implements `World` from `@workflow/world`: a storage, queue and stream backend the Workflow runtime talks to. It is not for application code. If the task is bumping an app's `workflow` dependency, use the `migrating-workflow-v4-to-v5` skill instead; if the app both uses Workflow and ships its own World, run that skill first and this one second.
An app on the Vercel, Local or Postgres World needs nothing from this skill. Those ship with the SDK and are already on the v5 spec.
One change dominates the work. **Event ID allocation is required, is not visible from the type signatures, and a World that skips it type-checks, starts runs, and fails on the first replay.** Do that part first, then the mechanical rewrites. Do not begin with the type errors: they are the small half, and finishing them produces a World that looks migrated and is not.
Which spec version a pin produces
What a World declares follows the `@workflow/world` version it resolves, and the runtime admits a range. Check both before deciding how much work this is.
| `@workflow/world` | `SPEC_VERSION_CURRENT` / `mintedSpecVersion()` | Admitted by `workflow` 5.2.x (range 6 to 8)? | | --- | --- | --- | | any 4.x | 5 or below | No | | `5.0.0-beta.25` and earlier | 5 or below (no slot identity) | No | | `5.0.0-beta.35` to `beta.37` | 7 | Yes, but not spec 8 | | `5.0.0-beta.39` and later, all 5.0.x stable | 8 | Yes |
The range is printed by the startup error, and lives in the bundled `@workflow/core` as `assertWorldSupportsRuntimeProtocol`. A host that bundles its own copy of the runtime has its own ceiling: grep the bundle for `supports Worlds with spec version` to read it. Declaring above a host's ceiling makes that host refuse the World at startup, so a World that must also run under older hosts states which host versions each of its releases supports.
**A World already on a 5.0.0 beta** with slot-numbered IDs does not redo step 1. Bump `@workflow/world` and `@workflow/errors` to the stable line, confirm the declaration is `mintedSpecVersion()` (step 2), read the spec 8 notes in step 2, then audit the step 5 contract items and the fail checklist: several of them landed late in the beta line and a beta-era World commonly misses lazy `step_started`, the `runs.list` status array and `resolveData: 'skip-step-inputs'`. Run the conformance suite at the new version before calling it done.
**A World that predates event sourcing** (it exposes `runs.create`, `steps.update` and the like, and has no `events.create` that drives entity state) has no v4 event log to port. The whole `events.create` state machine is written from scratch. Use `packages/world-postgres/src/storage.ts` as the reference and keep its event-by-event structure.
Intake
Before editing, establish and report each of these:
1. **Where the World is.** Grep for `implements World`, `: World`, `World>` and `from '@workflow/world'`. Read the factory it exports. 2. **How event IDs are minted today.** Grep for `eventId`, `ulid`, `uuid`, `nanoid`, `nextval`, `AUTO_INCREMENT`, `IDENTITY`. Find the exact line that produces the ID written to storage. Note any place an ID is chosen before the write that stores it, for example an ID planned in advance and saved in a claim record so redeliveries reuse it. That design hands out a position before the commit and needs reworking, not porting. 3. **What settles a write race.** Read the `events.create` implementation. Note whether the ID or ordering is decided in process (read-then-write, an in-memory counter, a `Math.max` over loaded events) or in the store (unique constraint, conditional write, `INSERT ... ON CONFLICT`, a transaction). 4. **Which `specVersion` it declares.** Grep for `specVersion`. Note whether it is a literal or an imported constant. 5. **Which optional members exist.** Grep for `capabilities`, `analytics`, `getRuntimeDeadline`, `getEnvironment`, `createRunId`, `describeRun`, `getEncryptionKeyForRun`, `resolveLatestDeploymentId`, `cancelMany`, `experimentalSetAttributes`. 6. **Whether it provisions step topics.** Grep for `'step'`, `__wkf_step`, `stepQueue`. 7. **Whether it rejects stale writes.** Grep for `PreconditionFailedError`, `preconditionGuard`, `stateUpdatedAt`, `stateEventCount`, `stateCursor`, `412`. 8. **Where its process-wide state lives.** Grep the World's modules for top-level `const`/`let` holding a pool, client, socket, registry, cache, ULID factory, or a log-once boolean. Note every one: these are correct in a `require()`d package and wrong in a bundled one. 9. **How it is tested.** Grep for `@workflow/world-testing` and `createTestSuite`. A World without the conformance suite wired up gets it in this migration. 10. **What its store can make atomic.** A transaction, a conditional write or unique constraint, a server-side script (Redis Lua, a Durable Object or actor that is single-threaded per run), or none (a CRDT or eventually consistent store). Step 1 depends on the answer. 11. **Whether bytes
Workflow SDK: Build durable, reliable, and observable apps and AI Agents in TypeScript
Other skills on workflow.
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about migrating-world-v4-to-v5.
- Which coding agents can use migrating-world-v4-to-v5?
- migrating-world-v4-to-v5 installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

