[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-vercel-migrating-world-v4-to-v5":3,"mdc--l26m4v-key":30,"related-org-vercel-migrating-world-v4-to-v5":2600,"related-repo-vercel-migrating-world-v4-to-v5":2779},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":19,"repoUrl":20,"updatedAt":21,"license":22,"forks":23,"topics":24,"repo":25,"sourceUrl":28,"mdContent":29},"migrating-world-v4-to-v5","upgrade Workflow SDK World to v5","Upgrades a custom Workflow SDK World implementation from the v4 spec to v5. Use when a package implements the `World` interface from `@workflow\u002Fworld` and is moving to 5.x — event IDs that are ULIDs rather than slot positions, `Event id is not slot-numbered` at replay time, a `specVersion` the runtime refuses, `writeToStream` \u002F `closeStream` \u002F `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` \u002F `createVercelWorld` factory.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"vercel","Vercel","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fvercel.png",[12,16],{"name":13,"slug":14,"type":15},"Workflow","workflow","tag",{"name":17,"slug":18,"type":15},"Migration","migration",2228,"https:\u002F\u002Fgithub.com\u002Fvercel\u002Fworkflow","2026-08-22T03:57:58.572694",null,307,[],{"repoUrl":20,"stars":19,"forks":23,"topics":26,"description":27},[],"Workflow SDK: Build durable, reliable, and observable apps and AI Agents in TypeScript","https:\u002F\u002Fgithub.com\u002Fvercel\u002Fworkflow\u002Ftree\u002FHEAD\u002Fskills\u002Fmigrating-world-v4-to-v5","---\nname: migrating-world-v4-to-v5\ndescription: Upgrades a custom Workflow SDK World implementation from the v4 spec to v5. Use when a package implements the `World` interface from `@workflow\u002Fworld` and is moving to 5.x — event IDs that are ULIDs rather than slot positions, `Event id is not slot-numbered` at replay time, a `specVersion` the runtime refuses, `writeToStream` \u002F `closeStream` \u002F `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` \u002F `createVercelWorld` factory.\nmetadata:\n  author: Vercel Inc.\n  version: '0.1.0'\n---\n\n# Migrating a World from the v4 spec to v5\n\nThis skill is for a package that implements `World` from `@workflow\u002Fworld`: 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.\n\nAn 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.\n\nOne 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.\n\n## Intake\n\nBefore editing, establish and report each of these:\n\n1. **Where the World is.** Grep for `implements World`, `: World`, `World>` and `from '@workflow\u002Fworld'`. Read the factory it exports.\n2. **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.\n3. **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).\n4. **Which `specVersion` it declares.** Grep for `specVersion`. Note whether it is a literal or an imported constant.\n5. **Which optional members exist.** Grep for `capabilities`, `analytics`, `getRuntimeDeadline`, `getEnvironment`, `createRunId`, `describeRun`, `getEncryptionKeyForRun`, `resolveLatestDeploymentId`, `cancelMany`, `experimentalSetAttributes`.\n6. **Whether it provisions step topics.** Grep for `'step'`, `__wkf_step`, `stepQueue`.\n7. **Whether it rejects stale writes.** Grep for `PreconditionFailedError`, `preconditionGuard`, `stateUpdatedAt`, `stateEventCount`, `stateCursor`, `412`.\n8. **How it is tested.** Grep for `@workflow\u002Fworld-testing` and `createTestSuite`. A World without the conformance suite wired up gets it in this migration.\n9. **Where its runs live.** Ask, or determine from the deployment model, whether a single deployment serves every run or a run is pinned to the deployment that created it. This decides the rollout in step 6 and cannot be read out of the code.\n\nReport anything not applicable rather than skipping it silently.\n\n## Step 1 — event ID allocation\n\nIn v4 an event ID was a ULID the World minted however it liked. In v5 an event ID is the event's **position in its run's log**: `evnt_` followed by a 1-based slot, zero-padded to 26 characters, so a run's first event is `evnt_00000000000000000000000001`.\n\n```ts\nimport { slotToEventId, eventIdToSlot, FIRST_EVENT_SLOT } from '@workflow\u002Fworld';\n\nslotToEventId(1); \u002F\u002F 'evnt_00000000000000000000000001'\neventIdToSlot('evnt_00000000000000000000000042'); \u002F\u002F 42\neventIdToSlot('evnt_01JQ...'); \u002F\u002F null\n```\n\nFormat IDs with `slotToEventId()`. Do not hand-roll the padding. The fixed width is what makes lexicographic order the same as positional order, so a World padding to a different width sorts its own log wrongly past ten events.\n\nThere is no capability to declare and no fallback path. The runtime calls `requireEventSlot()` on IDs it loads, which throws `Event id is not slot-numbered: \u003Cid>. This World allocates event positions the runtime cannot read.`\n\nFour rules bind the implementation. Check each against the code found in intake items 2 and 3:\n\n- **Uniqueness.** Two concurrent appends must not both take a slot. Settle it where the store settles it: a unique constraint on `(runId, eventId)`, a conditional write, or a serializable transaction. Reading the maximum slot and adding one in process is the failure mode this rule exists for, and it survives light testing because it only breaks under concurrency.\n- **Density.** Slots run from 1 with no holes. A writer that loses a race re-derives its slot from the store and takes the next free one. Incrementing a local number after a loss leaves a permanent hole, and the runtime fails the run with `CORRUPTED_EVENT_LOG` rather than replay across one.\n- **Bump and report.** `events.create()` params carry `eventCount`: how many events the writer held in the log it replayed from, so the slot it expects is `eventCount + 1`. When that slot is taken, **do not reject the write.** Commit at the next free slot, and return the events occupying the slots you skipped on the success response, in `events` with a matching `cursor` and `hasMore`. A stale count is the normal case for a parallel fan-out; rejecting it would serialize writes the runtime deliberately issues concurrently. A create that arrives with no `eventCount` came from a caller with no loaded log (a queued step body, an out-of-band writer) and is always accepted.\n- **Allocate at the commit.** Take the slot in the same operation that appends the event, never earlier. This is what makes a reader's log a *prefix* of the run's log rather than a prefix with a hole in it: nothing can land behind a slot a reader has already passed. A World that hands out a slot in a request handler and commits later breaks the property every replay depends on.\n\nThe shape that satisfies all four, for a SQL store with a unique key on `(run_id, event_id)`, is to compute the ID *inside* the insert and let the constraint arbitrate:\n\n```sql\nINSERT INTO events (run_id, event_id, event_type, data)\nSELECT $1,\n       'evnt_' || lpad((coalesce(\n         (SELECT cast(substring(prev.event_id from 6) AS bigint)\n            FROM events prev WHERE prev.run_id = $1\n           ORDER BY prev.event_id DESC LIMIT 1), 0) + 1)::text, 26, '0'),\n       $2, $3\nON CONFLICT (run_id, event_id) DO NOTHING\nRETURNING event_id;\n```\n\nNo row returned means another writer took that slot. Retry the same statement: it re-reads the maximum from a store that has already advanced, which is the bump. `@workflow\u002Fworld-postgres` does exactly this, with a bounded retry count and jittered backoff after the first few immediate attempts, and absorbs the conflict with `DO NOTHING` rather than raising, because these inserts run inside a transaction an error would poison.\n\nThe exact statement matters less than the property: the slot is computed and the row is inserted in one atomic operation against the rows the constraint protects, so a loser retries against the store rather than against a number it remembered. A store without conditional writes needs a serializable transaction instead, not an in-process lock, which only orders the writers inside one process.\n\nThen return the skipped span whenever the committed slot exceeds `eventCount + 1`. Understating `eventCount` is safe and overstating is not: a count below the writer's true position only widens the reported span, which the writer discards where its log already holds the events; a count above it makes the World report less than the writer is missing, which is a hole the writer never learns about.\n\n## Step 2 — declare the spec version\n\n`specVersion` is the protocol version the World implements, and the number stamped on every run it creates. Import the constant:\n\n```ts\nimport { SPEC_VERSION_CURRENT } from '@workflow\u002Fworld';\n\nexport function createWorld(): World {\n  return {\n    specVersion: SPEC_VERSION_CURRENT,\n    \u002F\u002F ...\n  };\n}\n```\n\nThe runtime checks this against `[SPEC_VERSION_CURRENT, SPEC_VERSION_MAX_SUPPORTED]` before it creates or replays anything and refuses a World outside that range, naming both the range and what the World declared. The floor sits where it does because slot-numbered IDs are required: a World declaring less allocates IDs the runtime cannot read positions out of.\n\nReplace a literal with the constant even when the numbers currently agree. A literal leaves the World a version behind the next bump and gets it rejected by the runtime it ships alongside. `SPEC_VERSION_SUPPORTS_SLOT_IDENTITY` is a literal by another name for this purpose: it names the version that introduced slots rather than the version to declare.\n\nRuns carry their own spec version, persisted at creation, and keep it for life. Read it off the run rather than assuming every run matches what the World declares today.\n\n## Step 3 — apply the mechanical rewrites\n\nThese are signature and module-shape changes. Apply each only where the pattern appears.\n\n### Streams moved to a `streams` namespace, with `runId` first\n\n| v4 | v5 |\n| --- | --- |\n| `writeToStream(name, runId, chunk)` | `streams.write(runId, name, chunk)` |\n| `writeToStreamMulti(name, runId, chunks)` | `streams.writeMulti(runId, name, chunks)` |\n| `closeStream(name, runId)` | `streams.close(runId, name)` |\n| `readFromStream(name, startIndex?)` | `streams.get(runId, name, startIndex?)` |\n| `getStreamChunks(name, runId, options?)` | `streams.getChunks(runId, name, options?)` |\n| `listStreamsByRunId(runId)` | `streams.list(runId)` |\n\nThe argument order flipped, so moving the methods without swapping arguments passes a stream name where a run ID is expected and type-checks whenever both are `string`. `readFromStream` had no `runId` at all; `streams.get` requires one, so thread the owning run through.\n\n`streams.streamFlushIntervalMs` sets the flush window. The v5 default is `0`, so the first chunk flushes immediately. Set a value only to deliberately coalesce writes.\n\n### `steps.get()` requires a `runId`\n\nThe first parameter was `string | undefined` and is now `string`. A World that looked a step up by ID alone needs the run in its key or its index.\n\n### `events.listByCorrelationId()` requires a `runId`\n\nA correlation ID identifies a step, hook or wait within its run, not across runs. Scope the lookup to one run. A World that paginates by event ID needs the run in its cursor comparison too, since two runs can now hold the same correlation ID. The same applies to `analytics.events.listByCorrelationId()`.\n\n### Export a `createWorld()` factory\n\n`createLocalWorld()` and `createVercelWorld()` are gone from the first-party packages, which now export `createWorld()`. Match that shape. The arguments are unchanged; this is a rename.\n\n### Worlds are injected at build time\n\nWorld selection is static, resolved into host bundles by the build rather than looked up dynamically at runtime. Verify the World still resolves after the upgrade and that its module graph survives bundling. A World that relied on a runtime `require` of a path computed from an environment variable will not be found.\n\n## Step 4 — the contract changes\n\nThese change no signature. A World ported by types alone compiles and then behaves incorrectly.\n\n- **Suspension dispatch is batched.** The asymmetric `{ timeoutSeconds }` wait-return contract is gone. A wait is an ordinary queue continuation carrying `delaySeconds`, and a suspension dispatches its waits and its steps as one parallel batch. A queue that assumed one message per suspension needs to handle the batch.\n- **Step queue topics are retired.** The `'step'` queue kind no longer exists. Queued steps travel on the workflow topic carrying `stepId` and `stepName` in the payload, and execute in the combined flow handler. Drop any `__wkf_step_*` topic provisioning.\n- **The `preconditionGuard` capability is gone, and so is the reason for it.** A World that rejected an event creation whose snapshot was behind the log can delete that code and its `stateUpdatedAt` \u002F `stateEventCount` \u002F `stateCursor` plumbing. Bump-and-report replaced it: a stale replay costs a merge instead of a rejection. `PreconditionFailedError` still exists for a World that allocates slots away from the commit and would rather refuse than report, which a World following step 1 is not.\n- **Capabilities fail closed.** An unadvertised capability costs performance, never correctness, so a partial World stays correct while it catches up. The reverse is not true: advertising something not enforced removes a guard the runtime was relying on. Set a flag only once the behavior is implemented.\n- **Event creation may return a delta.** `events.create()` may return events alongside the one it created, in `events` \u002F `cursor` \u002F `hasMore`. Beyond the bump-and-report case in step 1, the runtime uses this to skip a follow-up `events.list` on `run_started`, on step-terminal writes carrying `sinceCursor`, and on `hook_received` writes carrying `preloadEvents`. All three are advisory: returning only the created event stays correct and pays one more round trip.\n\n## Step 5 — optional surface worth adopting\n\nNone of this is required, and the runtime routes around each absence. Report what the World is missing rather than implementing everything unprompted.\n\n`capabilities` (`hookRetention.active`, `hookResumeDedup`, `deploymentAffinity`, `maxConcurrency`), `analytics`, `runs.experimentalSetAttributes`, `runs.cancelMany`, `getRuntimeDeadline()`, `getEnvironment()`, `createRunId()`, `describeRun()`, `getEncryptionKeyForRun()`, `resolveLatestDeploymentId()`, `close()`.\n\nTwo are worth raising unprompted because their absence is felt rather than reported. Without `getRuntimeDeadline()` the inline replay budget is a flat two minutes, so a host with a long function timeout does less work per invocation than it could. Without `close()`, CLI commands and short-lived processes cannot exit cleanly without `process.exit()`.\n\n## Step 6 — the rollout\n\nWarn the user, in the migration report, before they deploy:\n\n**Runs already in the store cannot be replayed by the new code.** A ULID-numbered run is not readable as positions, and the runtime refuses it rather than guessing. There is no mixed-scheme mode and no per-run fallback.\n\nWhich follows depends on intake item 9. Where a run executes on the deployment that created it, this resolves itself: those runs finish on the build that started them and never meet the new code. Where a single deployment serves every run, the in-flight ones must be drained on the 4.x build before the v5 World is deployed, or they will fail.\n\n## Verification\n\nWire up the conformance suite first. It is the cheapest way to catch the step 1 work being wrong:\n\n```ts\nimport { createTestSuite } from '@workflow\u002Fworld-testing';\n\ncreateTestSuite('@my-org\u002Fmy-world'); \u002F\u002F or a path to the built entrypoint\n```\n\nThe suite spawns a server with `WORKFLOW_TARGET_WORLD` set to that value and runs real workflows against it. Its `numbers events by position` test fails a World whose IDs do not decode to slots, whose run is not dense from 1, or whose IDs are not in canonical form. That turns the failure that would otherwise appear on a first replay into one line of test output.\n\nThen run, in order, and report actual output:\n\n1. Build and typecheck the World package.\n2. The conformance suite.\n3. The World's own test suite.\n4. An end-to-end run against a real app, covering: a run that suspends on a step and one that suspends on a wait; a parallel fan-out, so concurrent `events.create()` calls race for the same slot; a hook resumed after its run has progressed; a stream written and read back, including one closed before the reader attaches.\n\nConcurrency is the part that light testing misses. If the World's tests never issue two `events.create()` calls for the same run at once, add one that does before calling the migration done.\n\nFail the migration if any of these are true:\n\n- [ ] an event ID is produced by anything other than `slotToEventId()`\n- [ ] the slot is chosen by reading a maximum, or a counter, outside the operation that commits the event\n- [ ] the slot is handed out before the commit\n- [ ] `events.create()` rejects, throws or retries a write whose `eventCount + 1` slot was taken, instead of bumping to the next free slot\n- [ ] a bumped write returns without the skipped events on `events` \u002F `cursor` \u002F `hasMore`\n- [ ] a create carrying no `eventCount` is rejected\n- [ ] `specVersion` is a literal, or `SPEC_VERSION_SUPPORTS_SLOT_IDENTITY`, rather than `SPEC_VERSION_CURRENT`\n- [ ] a `streams.*` call kept the v4 argument order (name before runId)\n- [ ] `steps.get` or `listByCorrelationId` is reachable without a run ID\n- [ ] a capability is advertised whose behavior is not implemented\n- [ ] `@workflow\u002Fworld-testing` is not wired up, or its results were not reported\n- [ ] the rollout warning in step 6 was not given\n- [ ] the build, typecheck or test results were not actually run and reported\n\n## Required output shape\n\n```md\n## Summary\n## Event ID Allocation\n## Interface Changes\n## Contract Changes\n## Optional Surface Not Implemented\n## Rollout\n## Verification\n## Open Questions\n```\n\n- `## Event ID Allocation` states where the slot is now computed, what settles a race for it, and how a bumped write reports the span it skipped. Quote the code.\n- `## Optional Surface Not Implemented` lists what was left out and what each absence costs, so the user can decide.\n- `## Rollout` carries the step 6 warning and which of its two cases applies to this deployment.\n\n## Reference\n\n- Upgrading a World to v5: \u003Chttps:\u002F\u002Fworkflow.dev\u002Fworlds\u002Fupgrading-to-v5>\n- Building a World: \u003Chttps:\u002F\u002Fworkflow.dev\u002Fworlds\u002Fbuilding-a-world>\n- Event IDs: \u003Chttps:\u002F\u002Fworkflow.dev\u002Fdocs\u002Fhow-it-works\u002Fevent-sourcing#event-ids>\n- What's new in v5 (application-facing): \u003Chttps:\u002F\u002Fworkflow.dev\u002Fdocs\u002Fwhats-new>\n- Reference implementations: `packages\u002Fworld-local` and `packages\u002Fworld-postgres` in \u003Chttps:\u002F\u002Fgithub.com\u002Fvercel\u002Fworkflow>\n",{"data":31,"body":35},{"name":4,"description":6,"metadata":32},{"author":33,"version":34},"Vercel Inc.","0.1.0",{"type":36,"children":37},"root",[38,47,85,90,103,110,115,473,478,484,511,719,732,751,756,885,905,990,1011,1016,1035,1041,1051,1199,1212,1225,1230,1236,1241,1264,1418,1454,1473,1490,1510,1526,1538,1552,1577,1583,1596,1602,1607,1805,1811,1816,1924,1950,1956,1961,1971,1976,1982,1987,2078,2099,2104,2134,2146,2151,2366,2372,2478,2514,2520,2594],{"type":39,"tag":40,"props":41,"children":43},"element","h1",{"id":42},"migrating-a-world-from-the-v4-spec-to-v5",[44],{"type":45,"value":46},"text","Migrating a World from the v4 spec to v5",{"type":39,"tag":48,"props":49,"children":50},"p",{},[51,53,60,62,68,70,75,77,83],{"type":45,"value":52},"This skill is for a package that implements ",{"type":39,"tag":54,"props":55,"children":57},"code",{"className":56},[],[58],{"type":45,"value":59},"World",{"type":45,"value":61}," from ",{"type":39,"tag":54,"props":63,"children":65},{"className":64},[],[66],{"type":45,"value":67},"@workflow\u002Fworld",{"type":45,"value":69},": 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 ",{"type":39,"tag":54,"props":71,"children":73},{"className":72},[],[74],{"type":45,"value":14},{"type":45,"value":76}," dependency, use the ",{"type":39,"tag":54,"props":78,"children":80},{"className":79},[],[81],{"type":45,"value":82},"migrating-workflow-v4-to-v5",{"type":45,"value":84}," skill instead; if the app both uses Workflow and ships its own World, run that skill first and this one second.",{"type":39,"tag":48,"props":86,"children":87},{},[88],{"type":45,"value":89},"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.",{"type":39,"tag":48,"props":91,"children":92},{},[93,95,101],{"type":45,"value":94},"One change dominates the work. ",{"type":39,"tag":96,"props":97,"children":98},"strong",{},[99],{"type":45,"value":100},"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.",{"type":45,"value":102}," 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.",{"type":39,"tag":104,"props":105,"children":107},"h2",{"id":106},"intake",[108],{"type":45,"value":109},"Intake",{"type":39,"tag":48,"props":111,"children":112},{},[113],{"type":45,"value":114},"Before editing, establish and report each of these:",{"type":39,"tag":116,"props":117,"children":118},"ol",{},[119,161,220,254,278,358,388,439,463],{"type":39,"tag":120,"props":121,"children":122},"li",{},[123,128,130,136,138,144,145,151,153,159],{"type":39,"tag":96,"props":124,"children":125},{},[126],{"type":45,"value":127},"Where the World is.",{"type":45,"value":129}," Grep for ",{"type":39,"tag":54,"props":131,"children":133},{"className":132},[],[134],{"type":45,"value":135},"implements World",{"type":45,"value":137},", ",{"type":39,"tag":54,"props":139,"children":141},{"className":140},[],[142],{"type":45,"value":143},": World",{"type":45,"value":137},{"type":39,"tag":54,"props":146,"children":148},{"className":147},[],[149],{"type":45,"value":150},"World>",{"type":45,"value":152}," and ",{"type":39,"tag":54,"props":154,"children":156},{"className":155},[],[157],{"type":45,"value":158},"from '@workflow\u002Fworld'",{"type":45,"value":160},". Read the factory it exports.",{"type":39,"tag":120,"props":162,"children":163},{},[164,169,170,176,177,183,184,190,191,197,198,204,205,211,212,218],{"type":39,"tag":96,"props":165,"children":166},{},[167],{"type":45,"value":168},"How event IDs are minted today.",{"type":45,"value":129},{"type":39,"tag":54,"props":171,"children":173},{"className":172},[],[174],{"type":45,"value":175},"eventId",{"type":45,"value":137},{"type":39,"tag":54,"props":178,"children":180},{"className":179},[],[181],{"type":45,"value":182},"ulid",{"type":45,"value":137},{"type":39,"tag":54,"props":185,"children":187},{"className":186},[],[188],{"type":45,"value":189},"uuid",{"type":45,"value":137},{"type":39,"tag":54,"props":192,"children":194},{"className":193},[],[195],{"type":45,"value":196},"nanoid",{"type":45,"value":137},{"type":39,"tag":54,"props":199,"children":201},{"className":200},[],[202],{"type":45,"value":203},"nextval",{"type":45,"value":137},{"type":39,"tag":54,"props":206,"children":208},{"className":207},[],[209],{"type":45,"value":210},"AUTO_INCREMENT",{"type":45,"value":137},{"type":39,"tag":54,"props":213,"children":215},{"className":214},[],[216],{"type":45,"value":217},"IDENTITY",{"type":45,"value":219},". Find the exact line that produces the ID written to storage.",{"type":39,"tag":120,"props":221,"children":222},{},[223,228,230,236,238,244,246,252],{"type":39,"tag":96,"props":224,"children":225},{},[226],{"type":45,"value":227},"What settles a write race.",{"type":45,"value":229}," Read the ",{"type":39,"tag":54,"props":231,"children":233},{"className":232},[],[234],{"type":45,"value":235},"events.create",{"type":45,"value":237}," implementation. Note whether the ID or ordering is decided in process (read-then-write, an in-memory counter, a ",{"type":39,"tag":54,"props":239,"children":241},{"className":240},[],[242],{"type":45,"value":243},"Math.max",{"type":45,"value":245}," over loaded events) or in the store (unique constraint, conditional write, ",{"type":39,"tag":54,"props":247,"children":249},{"className":248},[],[250],{"type":45,"value":251},"INSERT ... ON CONFLICT",{"type":45,"value":253},", a transaction).",{"type":39,"tag":120,"props":255,"children":256},{},[257,270,271,276],{"type":39,"tag":96,"props":258,"children":259},{},[260,262,268],{"type":45,"value":261},"Which ",{"type":39,"tag":54,"props":263,"children":265},{"className":264},[],[266],{"type":45,"value":267},"specVersion",{"type":45,"value":269}," it declares.",{"type":45,"value":129},{"type":39,"tag":54,"props":272,"children":274},{"className":273},[],[275],{"type":45,"value":267},{"type":45,"value":277},". Note whether it is a literal or an imported constant.",{"type":39,"tag":120,"props":279,"children":280},{},[281,286,287,293,294,300,301,307,308,314,315,321,322,328,329,335,336,342,343,349,350,356],{"type":39,"tag":96,"props":282,"children":283},{},[284],{"type":45,"value":285},"Which optional members exist.",{"type":45,"value":129},{"type":39,"tag":54,"props":288,"children":290},{"className":289},[],[291],{"type":45,"value":292},"capabilities",{"type":45,"value":137},{"type":39,"tag":54,"props":295,"children":297},{"className":296},[],[298],{"type":45,"value":299},"analytics",{"type":45,"value":137},{"type":39,"tag":54,"props":302,"children":304},{"className":303},[],[305],{"type":45,"value":306},"getRuntimeDeadline",{"type":45,"value":137},{"type":39,"tag":54,"props":309,"children":311},{"className":310},[],[312],{"type":45,"value":313},"getEnvironment",{"type":45,"value":137},{"type":39,"tag":54,"props":316,"children":318},{"className":317},[],[319],{"type":45,"value":320},"createRunId",{"type":45,"value":137},{"type":39,"tag":54,"props":323,"children":325},{"className":324},[],[326],{"type":45,"value":327},"describeRun",{"type":45,"value":137},{"type":39,"tag":54,"props":330,"children":332},{"className":331},[],[333],{"type":45,"value":334},"getEncryptionKeyForRun",{"type":45,"value":137},{"type":39,"tag":54,"props":337,"children":339},{"className":338},[],[340],{"type":45,"value":341},"resolveLatestDeploymentId",{"type":45,"value":137},{"type":39,"tag":54,"props":344,"children":346},{"className":345},[],[347],{"type":45,"value":348},"cancelMany",{"type":45,"value":137},{"type":39,"tag":54,"props":351,"children":353},{"className":352},[],[354],{"type":45,"value":355},"experimentalSetAttributes",{"type":45,"value":357},".",{"type":39,"tag":120,"props":359,"children":360},{},[361,366,367,373,374,380,381,387],{"type":39,"tag":96,"props":362,"children":363},{},[364],{"type":45,"value":365},"Whether it provisions step topics.",{"type":45,"value":129},{"type":39,"tag":54,"props":368,"children":370},{"className":369},[],[371],{"type":45,"value":372},"'step'",{"type":45,"value":137},{"type":39,"tag":54,"props":375,"children":377},{"className":376},[],[378],{"type":45,"value":379},"__wkf_step",{"type":45,"value":137},{"type":39,"tag":54,"props":382,"children":384},{"className":383},[],[385],{"type":45,"value":386},"stepQueue",{"type":45,"value":357},{"type":39,"tag":120,"props":389,"children":390},{},[391,396,397,403,404,410,411,417,418,424,425,431,432,438],{"type":39,"tag":96,"props":392,"children":393},{},[394],{"type":45,"value":395},"Whether it rejects stale writes.",{"type":45,"value":129},{"type":39,"tag":54,"props":398,"children":400},{"className":399},[],[401],{"type":45,"value":402},"PreconditionFailedError",{"type":45,"value":137},{"type":39,"tag":54,"props":405,"children":407},{"className":406},[],[408],{"type":45,"value":409},"preconditionGuard",{"type":45,"value":137},{"type":39,"tag":54,"props":412,"children":414},{"className":413},[],[415],{"type":45,"value":416},"stateUpdatedAt",{"type":45,"value":137},{"type":39,"tag":54,"props":419,"children":421},{"className":420},[],[422],{"type":45,"value":423},"stateEventCount",{"type":45,"value":137},{"type":39,"tag":54,"props":426,"children":428},{"className":427},[],[429],{"type":45,"value":430},"stateCursor",{"type":45,"value":137},{"type":39,"tag":54,"props":433,"children":435},{"className":434},[],[436],{"type":45,"value":437},"412",{"type":45,"value":357},{"type":39,"tag":120,"props":440,"children":441},{},[442,447,448,454,455,461],{"type":39,"tag":96,"props":443,"children":444},{},[445],{"type":45,"value":446},"How it is tested.",{"type":45,"value":129},{"type":39,"tag":54,"props":449,"children":451},{"className":450},[],[452],{"type":45,"value":453},"@workflow\u002Fworld-testing",{"type":45,"value":152},{"type":39,"tag":54,"props":456,"children":458},{"className":457},[],[459],{"type":45,"value":460},"createTestSuite",{"type":45,"value":462},". A World without the conformance suite wired up gets it in this migration.",{"type":39,"tag":120,"props":464,"children":465},{},[466,471],{"type":39,"tag":96,"props":467,"children":468},{},[469],{"type":45,"value":470},"Where its runs live.",{"type":45,"value":472}," Ask, or determine from the deployment model, whether a single deployment serves every run or a run is pinned to the deployment that created it. This decides the rollout in step 6 and cannot be read out of the code.",{"type":39,"tag":48,"props":474,"children":475},{},[476],{"type":45,"value":477},"Report anything not applicable rather than skipping it silently.",{"type":39,"tag":104,"props":479,"children":481},{"id":480},"step-1-event-id-allocation",[482],{"type":45,"value":483},"Step 1 — event ID allocation",{"type":39,"tag":48,"props":485,"children":486},{},[487,489,494,496,502,504,510],{"type":45,"value":488},"In v4 an event ID was a ULID the World minted however it liked. In v5 an event ID is the event's ",{"type":39,"tag":96,"props":490,"children":491},{},[492],{"type":45,"value":493},"position in its run's log",{"type":45,"value":495},": ",{"type":39,"tag":54,"props":497,"children":499},{"className":498},[],[500],{"type":45,"value":501},"evnt_",{"type":45,"value":503}," followed by a 1-based slot, zero-padded to 26 characters, so a run's first event is ",{"type":39,"tag":54,"props":505,"children":507},{"className":506},[],[508],{"type":45,"value":509},"evnt_00000000000000000000000001",{"type":45,"value":357},{"type":39,"tag":512,"props":513,"children":518},"pre",{"className":514,"code":515,"language":516,"meta":517,"style":517},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { slotToEventId, eventIdToSlot, FIRST_EVENT_SLOT } from '@workflow\u002Fworld';\n\nslotToEventId(1); \u002F\u002F 'evnt_00000000000000000000000001'\neventIdToSlot('evnt_00000000000000000000000042'); \u002F\u002F 42\neventIdToSlot('evnt_01JQ...'); \u002F\u002F null\n","ts","",[519],{"type":39,"tag":54,"props":520,"children":521},{"__ignoreMap":517},[522,595,605,642,681],{"type":39,"tag":523,"props":524,"children":527},"span",{"class":525,"line":526},"line",1,[528,534,540,546,551,556,560,565,570,575,580,585,590],{"type":39,"tag":523,"props":529,"children":531},{"style":530},"--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic",[532],{"type":45,"value":533},"import",{"type":39,"tag":523,"props":535,"children":537},{"style":536},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[538],{"type":45,"value":539}," {",{"type":39,"tag":523,"props":541,"children":543},{"style":542},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[544],{"type":45,"value":545}," slotToEventId",{"type":39,"tag":523,"props":547,"children":548},{"style":536},[549],{"type":45,"value":550},",",{"type":39,"tag":523,"props":552,"children":553},{"style":542},[554],{"type":45,"value":555}," eventIdToSlot",{"type":39,"tag":523,"props":557,"children":558},{"style":536},[559],{"type":45,"value":550},{"type":39,"tag":523,"props":561,"children":562},{"style":542},[563],{"type":45,"value":564}," FIRST_EVENT_SLOT",{"type":39,"tag":523,"props":566,"children":567},{"style":536},[568],{"type":45,"value":569}," }",{"type":39,"tag":523,"props":571,"children":572},{"style":530},[573],{"type":45,"value":574}," from",{"type":39,"tag":523,"props":576,"children":577},{"style":536},[578],{"type":45,"value":579}," '",{"type":39,"tag":523,"props":581,"children":583},{"style":582},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[584],{"type":45,"value":67},{"type":39,"tag":523,"props":586,"children":587},{"style":536},[588],{"type":45,"value":589},"'",{"type":39,"tag":523,"props":591,"children":592},{"style":536},[593],{"type":45,"value":594},";\n",{"type":39,"tag":523,"props":596,"children":598},{"class":525,"line":597},2,[599],{"type":39,"tag":523,"props":600,"children":602},{"emptyLinePlaceholder":601},true,[603],{"type":45,"value":604},"\n",{"type":39,"tag":523,"props":606,"children":608},{"class":525,"line":607},3,[609,615,620,626,631,636],{"type":39,"tag":523,"props":610,"children":612},{"style":611},"--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF",[613],{"type":45,"value":614},"slotToEventId",{"type":39,"tag":523,"props":616,"children":617},{"style":542},[618],{"type":45,"value":619},"(",{"type":39,"tag":523,"props":621,"children":623},{"style":622},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[624],{"type":45,"value":625},"1",{"type":39,"tag":523,"props":627,"children":628},{"style":542},[629],{"type":45,"value":630},")",{"type":39,"tag":523,"props":632,"children":633},{"style":536},[634],{"type":45,"value":635},";",{"type":39,"tag":523,"props":637,"children":639},{"style":638},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[640],{"type":45,"value":641}," \u002F\u002F 'evnt_00000000000000000000000001'\n",{"type":39,"tag":523,"props":643,"children":645},{"class":525,"line":644},4,[646,651,655,659,664,668,672,676],{"type":39,"tag":523,"props":647,"children":648},{"style":611},[649],{"type":45,"value":650},"eventIdToSlot",{"type":39,"tag":523,"props":652,"children":653},{"style":542},[654],{"type":45,"value":619},{"type":39,"tag":523,"props":656,"children":657},{"style":536},[658],{"type":45,"value":589},{"type":39,"tag":523,"props":660,"children":661},{"style":582},[662],{"type":45,"value":663},"evnt_00000000000000000000000042",{"type":39,"tag":523,"props":665,"children":666},{"style":536},[667],{"type":45,"value":589},{"type":39,"tag":523,"props":669,"children":670},{"style":542},[671],{"type":45,"value":630},{"type":39,"tag":523,"props":673,"children":674},{"style":536},[675],{"type":45,"value":635},{"type":39,"tag":523,"props":677,"children":678},{"style":638},[679],{"type":45,"value":680}," \u002F\u002F 42\n",{"type":39,"tag":523,"props":682,"children":684},{"class":525,"line":683},5,[685,689,693,697,702,706,710,714],{"type":39,"tag":523,"props":686,"children":687},{"style":611},[688],{"type":45,"value":650},{"type":39,"tag":523,"props":690,"children":691},{"style":542},[692],{"type":45,"value":619},{"type":39,"tag":523,"props":694,"children":695},{"style":536},[696],{"type":45,"value":589},{"type":39,"tag":523,"props":698,"children":699},{"style":582},[700],{"type":45,"value":701},"evnt_01JQ...",{"type":39,"tag":523,"props":703,"children":704},{"style":536},[705],{"type":45,"value":589},{"type":39,"tag":523,"props":707,"children":708},{"style":542},[709],{"type":45,"value":630},{"type":39,"tag":523,"props":711,"children":712},{"style":536},[713],{"type":45,"value":635},{"type":39,"tag":523,"props":715,"children":716},{"style":638},[717],{"type":45,"value":718}," \u002F\u002F null\n",{"type":39,"tag":48,"props":720,"children":721},{},[722,724,730],{"type":45,"value":723},"Format IDs with ",{"type":39,"tag":54,"props":725,"children":727},{"className":726},[],[728],{"type":45,"value":729},"slotToEventId()",{"type":45,"value":731},". Do not hand-roll the padding. The fixed width is what makes lexicographic order the same as positional order, so a World padding to a different width sorts its own log wrongly past ten events.",{"type":39,"tag":48,"props":733,"children":734},{},[735,737,743,745],{"type":45,"value":736},"There is no capability to declare and no fallback path. The runtime calls ",{"type":39,"tag":54,"props":738,"children":740},{"className":739},[],[741],{"type":45,"value":742},"requireEventSlot()",{"type":45,"value":744}," on IDs it loads, which throws ",{"type":39,"tag":54,"props":746,"children":748},{"className":747},[],[749],{"type":45,"value":750},"Event id is not slot-numbered: \u003Cid>. This World allocates event positions the runtime cannot read.",{"type":39,"tag":48,"props":752,"children":753},{},[754],{"type":45,"value":755},"Four rules bind the implementation. Check each against the code found in intake items 2 and 3:",{"type":39,"tag":757,"props":758,"children":759},"ul",{},[760,778,796,867],{"type":39,"tag":120,"props":761,"children":762},{},[763,768,770,776],{"type":39,"tag":96,"props":764,"children":765},{},[766],{"type":45,"value":767},"Uniqueness.",{"type":45,"value":769}," Two concurrent appends must not both take a slot. Settle it where the store settles it: a unique constraint on ",{"type":39,"tag":54,"props":771,"children":773},{"className":772},[],[774],{"type":45,"value":775},"(runId, eventId)",{"type":45,"value":777},", a conditional write, or a serializable transaction. Reading the maximum slot and adding one in process is the failure mode this rule exists for, and it survives light testing because it only breaks under concurrency.",{"type":39,"tag":120,"props":779,"children":780},{},[781,786,788,794],{"type":39,"tag":96,"props":782,"children":783},{},[784],{"type":45,"value":785},"Density.",{"type":45,"value":787}," Slots run from 1 with no holes. A writer that loses a race re-derives its slot from the store and takes the next free one. Incrementing a local number after a loss leaves a permanent hole, and the runtime fails the run with ",{"type":39,"tag":54,"props":789,"children":791},{"className":790},[],[792],{"type":45,"value":793},"CORRUPTED_EVENT_LOG",{"type":45,"value":795}," rather than replay across one.",{"type":39,"tag":120,"props":797,"children":798},{},[799,804,806,812,814,820,822,828,830,835,837,843,845,851,852,858,860,865],{"type":39,"tag":96,"props":800,"children":801},{},[802],{"type":45,"value":803},"Bump and report.",{"type":45,"value":805}," ",{"type":39,"tag":54,"props":807,"children":809},{"className":808},[],[810],{"type":45,"value":811},"events.create()",{"type":45,"value":813}," params carry ",{"type":39,"tag":54,"props":815,"children":817},{"className":816},[],[818],{"type":45,"value":819},"eventCount",{"type":45,"value":821},": how many events the writer held in the log it replayed from, so the slot it expects is ",{"type":39,"tag":54,"props":823,"children":825},{"className":824},[],[826],{"type":45,"value":827},"eventCount + 1",{"type":45,"value":829},". When that slot is taken, ",{"type":39,"tag":96,"props":831,"children":832},{},[833],{"type":45,"value":834},"do not reject the write.",{"type":45,"value":836}," Commit at the next free slot, and return the events occupying the slots you skipped on the success response, in ",{"type":39,"tag":54,"props":838,"children":840},{"className":839},[],[841],{"type":45,"value":842},"events",{"type":45,"value":844}," with a matching ",{"type":39,"tag":54,"props":846,"children":848},{"className":847},[],[849],{"type":45,"value":850},"cursor",{"type":45,"value":152},{"type":39,"tag":54,"props":853,"children":855},{"className":854},[],[856],{"type":45,"value":857},"hasMore",{"type":45,"value":859},". A stale count is the normal case for a parallel fan-out; rejecting it would serialize writes the runtime deliberately issues concurrently. A create that arrives with no ",{"type":39,"tag":54,"props":861,"children":863},{"className":862},[],[864],{"type":45,"value":819},{"type":45,"value":866}," came from a caller with no loaded log (a queued step body, an out-of-band writer) and is always accepted.",{"type":39,"tag":120,"props":868,"children":869},{},[870,875,877,883],{"type":39,"tag":96,"props":871,"children":872},{},[873],{"type":45,"value":874},"Allocate at the commit.",{"type":45,"value":876}," Take the slot in the same operation that appends the event, never earlier. This is what makes a reader's log a ",{"type":39,"tag":878,"props":879,"children":880},"em",{},[881],{"type":45,"value":882},"prefix",{"type":45,"value":884}," of the run's log rather than a prefix with a hole in it: nothing can land behind a slot a reader has already passed. A World that hands out a slot in a request handler and commits later breaks the property every replay depends on.",{"type":39,"tag":48,"props":886,"children":887},{},[888,890,896,898,903],{"type":45,"value":889},"The shape that satisfies all four, for a SQL store with a unique key on ",{"type":39,"tag":54,"props":891,"children":893},{"className":892},[],[894],{"type":45,"value":895},"(run_id, event_id)",{"type":45,"value":897},", is to compute the ID ",{"type":39,"tag":878,"props":899,"children":900},{},[901],{"type":45,"value":902},"inside",{"type":45,"value":904}," the insert and let the constraint arbitrate:",{"type":39,"tag":512,"props":906,"children":910},{"className":907,"code":908,"language":909,"meta":517,"style":517},"language-sql shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","INSERT INTO events (run_id, event_id, event_type, data)\nSELECT $1,\n       'evnt_' || lpad((coalesce(\n         (SELECT cast(substring(prev.event_id from 6) AS bigint)\n            FROM events prev WHERE prev.run_id = $1\n           ORDER BY prev.event_id DESC LIMIT 1), 0) + 1)::text, 26, '0'),\n       $2, $3\nON CONFLICT (run_id, event_id) DO NOTHING\nRETURNING event_id;\n","sql",[911],{"type":39,"tag":54,"props":912,"children":913},{"__ignoreMap":517},[914,922,930,938,946,954,963,972,981],{"type":39,"tag":523,"props":915,"children":916},{"class":525,"line":526},[917],{"type":39,"tag":523,"props":918,"children":919},{},[920],{"type":45,"value":921},"INSERT INTO events (run_id, event_id, event_type, data)\n",{"type":39,"tag":523,"props":923,"children":924},{"class":525,"line":597},[925],{"type":39,"tag":523,"props":926,"children":927},{},[928],{"type":45,"value":929},"SELECT $1,\n",{"type":39,"tag":523,"props":931,"children":932},{"class":525,"line":607},[933],{"type":39,"tag":523,"props":934,"children":935},{},[936],{"type":45,"value":937},"       'evnt_' || lpad((coalesce(\n",{"type":39,"tag":523,"props":939,"children":940},{"class":525,"line":644},[941],{"type":39,"tag":523,"props":942,"children":943},{},[944],{"type":45,"value":945},"         (SELECT cast(substring(prev.event_id from 6) AS bigint)\n",{"type":39,"tag":523,"props":947,"children":948},{"class":525,"line":683},[949],{"type":39,"tag":523,"props":950,"children":951},{},[952],{"type":45,"value":953},"            FROM events prev WHERE prev.run_id = $1\n",{"type":39,"tag":523,"props":955,"children":957},{"class":525,"line":956},6,[958],{"type":39,"tag":523,"props":959,"children":960},{},[961],{"type":45,"value":962},"           ORDER BY prev.event_id DESC LIMIT 1), 0) + 1)::text, 26, '0'),\n",{"type":39,"tag":523,"props":964,"children":966},{"class":525,"line":965},7,[967],{"type":39,"tag":523,"props":968,"children":969},{},[970],{"type":45,"value":971},"       $2, $3\n",{"type":39,"tag":523,"props":973,"children":975},{"class":525,"line":974},8,[976],{"type":39,"tag":523,"props":977,"children":978},{},[979],{"type":45,"value":980},"ON CONFLICT (run_id, event_id) DO NOTHING\n",{"type":39,"tag":523,"props":982,"children":984},{"class":525,"line":983},9,[985],{"type":39,"tag":523,"props":986,"children":987},{},[988],{"type":45,"value":989},"RETURNING event_id;\n",{"type":39,"tag":48,"props":991,"children":992},{},[993,995,1001,1003,1009],{"type":45,"value":994},"No row returned means another writer took that slot. Retry the same statement: it re-reads the maximum from a store that has already advanced, which is the bump. ",{"type":39,"tag":54,"props":996,"children":998},{"className":997},[],[999],{"type":45,"value":1000},"@workflow\u002Fworld-postgres",{"type":45,"value":1002}," does exactly this, with a bounded retry count and jittered backoff after the first few immediate attempts, and absorbs the conflict with ",{"type":39,"tag":54,"props":1004,"children":1006},{"className":1005},[],[1007],{"type":45,"value":1008},"DO NOTHING",{"type":45,"value":1010}," rather than raising, because these inserts run inside a transaction an error would poison.",{"type":39,"tag":48,"props":1012,"children":1013},{},[1014],{"type":45,"value":1015},"The exact statement matters less than the property: the slot is computed and the row is inserted in one atomic operation against the rows the constraint protects, so a loser retries against the store rather than against a number it remembered. A store without conditional writes needs a serializable transaction instead, not an in-process lock, which only orders the writers inside one process.",{"type":39,"tag":48,"props":1017,"children":1018},{},[1019,1021,1026,1028,1033],{"type":45,"value":1020},"Then return the skipped span whenever the committed slot exceeds ",{"type":39,"tag":54,"props":1022,"children":1024},{"className":1023},[],[1025],{"type":45,"value":827},{"type":45,"value":1027},". Understating ",{"type":39,"tag":54,"props":1029,"children":1031},{"className":1030},[],[1032],{"type":45,"value":819},{"type":45,"value":1034}," is safe and overstating is not: a count below the writer's true position only widens the reported span, which the writer discards where its log already holds the events; a count above it makes the World report less than the writer is missing, which is a hole the writer never learns about.",{"type":39,"tag":104,"props":1036,"children":1038},{"id":1037},"step-2-declare-the-spec-version",[1039],{"type":45,"value":1040},"Step 2 — declare the spec version",{"type":39,"tag":48,"props":1042,"children":1043},{},[1044,1049],{"type":39,"tag":54,"props":1045,"children":1047},{"className":1046},[],[1048],{"type":45,"value":267},{"type":45,"value":1050}," is the protocol version the World implements, and the number stamped on every run it creates. Import the constant:",{"type":39,"tag":512,"props":1052,"children":1054},{"className":514,"code":1053,"language":516,"meta":517,"style":517},"import { SPEC_VERSION_CURRENT } from '@workflow\u002Fworld';\n\nexport function createWorld(): World {\n  return {\n    specVersion: SPEC_VERSION_CURRENT,\n    \u002F\u002F ...\n  };\n}\n",[1055],{"type":39,"tag":54,"props":1056,"children":1057},{"__ignoreMap":517},[1058,1098,1105,1140,1152,1175,1183,1191],{"type":39,"tag":523,"props":1059,"children":1060},{"class":525,"line":526},[1061,1065,1069,1074,1078,1082,1086,1090,1094],{"type":39,"tag":523,"props":1062,"children":1063},{"style":530},[1064],{"type":45,"value":533},{"type":39,"tag":523,"props":1066,"children":1067},{"style":536},[1068],{"type":45,"value":539},{"type":39,"tag":523,"props":1070,"children":1071},{"style":542},[1072],{"type":45,"value":1073}," SPEC_VERSION_CURRENT",{"type":39,"tag":523,"props":1075,"children":1076},{"style":536},[1077],{"type":45,"value":569},{"type":39,"tag":523,"props":1079,"children":1080},{"style":530},[1081],{"type":45,"value":574},{"type":39,"tag":523,"props":1083,"children":1084},{"style":536},[1085],{"type":45,"value":579},{"type":39,"tag":523,"props":1087,"children":1088},{"style":582},[1089],{"type":45,"value":67},{"type":39,"tag":523,"props":1091,"children":1092},{"style":536},[1093],{"type":45,"value":589},{"type":39,"tag":523,"props":1095,"children":1096},{"style":536},[1097],{"type":45,"value":594},{"type":39,"tag":523,"props":1099,"children":1100},{"class":525,"line":597},[1101],{"type":39,"tag":523,"props":1102,"children":1103},{"emptyLinePlaceholder":601},[1104],{"type":45,"value":604},{"type":39,"tag":523,"props":1106,"children":1107},{"class":525,"line":607},[1108,1113,1119,1124,1129,1135],{"type":39,"tag":523,"props":1109,"children":1110},{"style":530},[1111],{"type":45,"value":1112},"export",{"type":39,"tag":523,"props":1114,"children":1116},{"style":1115},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[1117],{"type":45,"value":1118}," function",{"type":39,"tag":523,"props":1120,"children":1121},{"style":611},[1122],{"type":45,"value":1123}," createWorld",{"type":39,"tag":523,"props":1125,"children":1126},{"style":536},[1127],{"type":45,"value":1128},"():",{"type":39,"tag":523,"props":1130,"children":1132},{"style":1131},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[1133],{"type":45,"value":1134}," World",{"type":39,"tag":523,"props":1136,"children":1137},{"style":536},[1138],{"type":45,"value":1139}," {\n",{"type":39,"tag":523,"props":1141,"children":1142},{"class":525,"line":644},[1143,1148],{"type":39,"tag":523,"props":1144,"children":1145},{"style":530},[1146],{"type":45,"value":1147},"  return",{"type":39,"tag":523,"props":1149,"children":1150},{"style":536},[1151],{"type":45,"value":1139},{"type":39,"tag":523,"props":1153,"children":1154},{"class":525,"line":683},[1155,1161,1166,1170],{"type":39,"tag":523,"props":1156,"children":1158},{"style":1157},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[1159],{"type":45,"value":1160},"    specVersion",{"type":39,"tag":523,"props":1162,"children":1163},{"style":536},[1164],{"type":45,"value":1165},":",{"type":39,"tag":523,"props":1167,"children":1168},{"style":542},[1169],{"type":45,"value":1073},{"type":39,"tag":523,"props":1171,"children":1172},{"style":536},[1173],{"type":45,"value":1174},",\n",{"type":39,"tag":523,"props":1176,"children":1177},{"class":525,"line":956},[1178],{"type":39,"tag":523,"props":1179,"children":1180},{"style":638},[1181],{"type":45,"value":1182},"    \u002F\u002F ...\n",{"type":39,"tag":523,"props":1184,"children":1185},{"class":525,"line":965},[1186],{"type":39,"tag":523,"props":1187,"children":1188},{"style":536},[1189],{"type":45,"value":1190},"  };\n",{"type":39,"tag":523,"props":1192,"children":1193},{"class":525,"line":974},[1194],{"type":39,"tag":523,"props":1195,"children":1196},{"style":536},[1197],{"type":45,"value":1198},"}\n",{"type":39,"tag":48,"props":1200,"children":1201},{},[1202,1204,1210],{"type":45,"value":1203},"The runtime checks this against ",{"type":39,"tag":54,"props":1205,"children":1207},{"className":1206},[],[1208],{"type":45,"value":1209},"[SPEC_VERSION_CURRENT, SPEC_VERSION_MAX_SUPPORTED]",{"type":45,"value":1211}," before it creates or replays anything and refuses a World outside that range, naming both the range and what the World declared. The floor sits where it does because slot-numbered IDs are required: a World declaring less allocates IDs the runtime cannot read positions out of.",{"type":39,"tag":48,"props":1213,"children":1214},{},[1215,1217,1223],{"type":45,"value":1216},"Replace a literal with the constant even when the numbers currently agree. A literal leaves the World a version behind the next bump and gets it rejected by the runtime it ships alongside. ",{"type":39,"tag":54,"props":1218,"children":1220},{"className":1219},[],[1221],{"type":45,"value":1222},"SPEC_VERSION_SUPPORTS_SLOT_IDENTITY",{"type":45,"value":1224}," is a literal by another name for this purpose: it names the version that introduced slots rather than the version to declare.",{"type":39,"tag":48,"props":1226,"children":1227},{},[1228],{"type":45,"value":1229},"Runs carry their own spec version, persisted at creation, and keep it for life. Read it off the run rather than assuming every run matches what the World declares today.",{"type":39,"tag":104,"props":1231,"children":1233},{"id":1232},"step-3-apply-the-mechanical-rewrites",[1234],{"type":45,"value":1235},"Step 3 — apply the mechanical rewrites",{"type":39,"tag":48,"props":1237,"children":1238},{},[1239],{"type":45,"value":1240},"These are signature and module-shape changes. Apply each only where the pattern appears.",{"type":39,"tag":1242,"props":1243,"children":1245},"h3",{"id":1244},"streams-moved-to-a-streams-namespace-with-runid-first",[1246,1248,1254,1256,1262],{"type":45,"value":1247},"Streams moved to a ",{"type":39,"tag":54,"props":1249,"children":1251},{"className":1250},[],[1252],{"type":45,"value":1253},"streams",{"type":45,"value":1255}," namespace, with ",{"type":39,"tag":54,"props":1257,"children":1259},{"className":1258},[],[1260],{"type":45,"value":1261},"runId",{"type":45,"value":1263}," first",{"type":39,"tag":1265,"props":1266,"children":1267},"table",{},[1268,1287],{"type":39,"tag":1269,"props":1270,"children":1271},"thead",{},[1272],{"type":39,"tag":1273,"props":1274,"children":1275},"tr",{},[1276,1282],{"type":39,"tag":1277,"props":1278,"children":1279},"th",{},[1280],{"type":45,"value":1281},"v4",{"type":39,"tag":1277,"props":1283,"children":1284},{},[1285],{"type":45,"value":1286},"v5",{"type":39,"tag":1288,"props":1289,"children":1290},"tbody",{},[1291,1313,1334,1355,1376,1397],{"type":39,"tag":1273,"props":1292,"children":1293},{},[1294,1304],{"type":39,"tag":1295,"props":1296,"children":1297},"td",{},[1298],{"type":39,"tag":54,"props":1299,"children":1301},{"className":1300},[],[1302],{"type":45,"value":1303},"writeToStream(name, runId, chunk)",{"type":39,"tag":1295,"props":1305,"children":1306},{},[1307],{"type":39,"tag":54,"props":1308,"children":1310},{"className":1309},[],[1311],{"type":45,"value":1312},"streams.write(runId, name, chunk)",{"type":39,"tag":1273,"props":1314,"children":1315},{},[1316,1325],{"type":39,"tag":1295,"props":1317,"children":1318},{},[1319],{"type":39,"tag":54,"props":1320,"children":1322},{"className":1321},[],[1323],{"type":45,"value":1324},"writeToStreamMulti(name, runId, chunks)",{"type":39,"tag":1295,"props":1326,"children":1327},{},[1328],{"type":39,"tag":54,"props":1329,"children":1331},{"className":1330},[],[1332],{"type":45,"value":1333},"streams.writeMulti(runId, name, chunks)",{"type":39,"tag":1273,"props":1335,"children":1336},{},[1337,1346],{"type":39,"tag":1295,"props":1338,"children":1339},{},[1340],{"type":39,"tag":54,"props":1341,"children":1343},{"className":1342},[],[1344],{"type":45,"value":1345},"closeStream(name, runId)",{"type":39,"tag":1295,"props":1347,"children":1348},{},[1349],{"type":39,"tag":54,"props":1350,"children":1352},{"className":1351},[],[1353],{"type":45,"value":1354},"streams.close(runId, name)",{"type":39,"tag":1273,"props":1356,"children":1357},{},[1358,1367],{"type":39,"tag":1295,"props":1359,"children":1360},{},[1361],{"type":39,"tag":54,"props":1362,"children":1364},{"className":1363},[],[1365],{"type":45,"value":1366},"readFromStream(name, startIndex?)",{"type":39,"tag":1295,"props":1368,"children":1369},{},[1370],{"type":39,"tag":54,"props":1371,"children":1373},{"className":1372},[],[1374],{"type":45,"value":1375},"streams.get(runId, name, startIndex?)",{"type":39,"tag":1273,"props":1377,"children":1378},{},[1379,1388],{"type":39,"tag":1295,"props":1380,"children":1381},{},[1382],{"type":39,"tag":54,"props":1383,"children":1385},{"className":1384},[],[1386],{"type":45,"value":1387},"getStreamChunks(name, runId, options?)",{"type":39,"tag":1295,"props":1389,"children":1390},{},[1391],{"type":39,"tag":54,"props":1392,"children":1394},{"className":1393},[],[1395],{"type":45,"value":1396},"streams.getChunks(runId, name, options?)",{"type":39,"tag":1273,"props":1398,"children":1399},{},[1400,1409],{"type":39,"tag":1295,"props":1401,"children":1402},{},[1403],{"type":39,"tag":54,"props":1404,"children":1406},{"className":1405},[],[1407],{"type":45,"value":1408},"listStreamsByRunId(runId)",{"type":39,"tag":1295,"props":1410,"children":1411},{},[1412],{"type":39,"tag":54,"props":1413,"children":1415},{"className":1414},[],[1416],{"type":45,"value":1417},"streams.list(runId)",{"type":39,"tag":48,"props":1419,"children":1420},{},[1421,1423,1429,1431,1437,1439,1444,1446,1452],{"type":45,"value":1422},"The argument order flipped, so moving the methods without swapping arguments passes a stream name where a run ID is expected and type-checks whenever both are ",{"type":39,"tag":54,"props":1424,"children":1426},{"className":1425},[],[1427],{"type":45,"value":1428},"string",{"type":45,"value":1430},". ",{"type":39,"tag":54,"props":1432,"children":1434},{"className":1433},[],[1435],{"type":45,"value":1436},"readFromStream",{"type":45,"value":1438}," had no ",{"type":39,"tag":54,"props":1440,"children":1442},{"className":1441},[],[1443],{"type":45,"value":1261},{"type":45,"value":1445}," at all; ",{"type":39,"tag":54,"props":1447,"children":1449},{"className":1448},[],[1450],{"type":45,"value":1451},"streams.get",{"type":45,"value":1453}," requires one, so thread the owning run through.",{"type":39,"tag":48,"props":1455,"children":1456},{},[1457,1463,1465,1471],{"type":39,"tag":54,"props":1458,"children":1460},{"className":1459},[],[1461],{"type":45,"value":1462},"streams.streamFlushIntervalMs",{"type":45,"value":1464}," sets the flush window. The v5 default is ",{"type":39,"tag":54,"props":1466,"children":1468},{"className":1467},[],[1469],{"type":45,"value":1470},"0",{"type":45,"value":1472},", so the first chunk flushes immediately. Set a value only to deliberately coalesce writes.",{"type":39,"tag":1242,"props":1474,"children":1476},{"id":1475},"stepsget-requires-a-runid",[1477,1483,1485],{"type":39,"tag":54,"props":1478,"children":1480},{"className":1479},[],[1481],{"type":45,"value":1482},"steps.get()",{"type":45,"value":1484}," requires a ",{"type":39,"tag":54,"props":1486,"children":1488},{"className":1487},[],[1489],{"type":45,"value":1261},{"type":39,"tag":48,"props":1491,"children":1492},{},[1493,1495,1501,1503,1508],{"type":45,"value":1494},"The first parameter was ",{"type":39,"tag":54,"props":1496,"children":1498},{"className":1497},[],[1499],{"type":45,"value":1500},"string | undefined",{"type":45,"value":1502}," and is now ",{"type":39,"tag":54,"props":1504,"children":1506},{"className":1505},[],[1507],{"type":45,"value":1428},{"type":45,"value":1509},". A World that looked a step up by ID alone needs the run in its key or its index.",{"type":39,"tag":1242,"props":1511,"children":1513},{"id":1512},"eventslistbycorrelationid-requires-a-runid",[1514,1520,1521],{"type":39,"tag":54,"props":1515,"children":1517},{"className":1516},[],[1518],{"type":45,"value":1519},"events.listByCorrelationId()",{"type":45,"value":1484},{"type":39,"tag":54,"props":1522,"children":1524},{"className":1523},[],[1525],{"type":45,"value":1261},{"type":39,"tag":48,"props":1527,"children":1528},{},[1529,1531,1537],{"type":45,"value":1530},"A correlation ID identifies a step, hook or wait within its run, not across runs. Scope the lookup to one run. A World that paginates by event ID needs the run in its cursor comparison too, since two runs can now hold the same correlation ID. The same applies to ",{"type":39,"tag":54,"props":1532,"children":1534},{"className":1533},[],[1535],{"type":45,"value":1536},"analytics.events.listByCorrelationId()",{"type":45,"value":357},{"type":39,"tag":1242,"props":1539,"children":1541},{"id":1540},"export-a-createworld-factory",[1542,1544,1550],{"type":45,"value":1543},"Export a ",{"type":39,"tag":54,"props":1545,"children":1547},{"className":1546},[],[1548],{"type":45,"value":1549},"createWorld()",{"type":45,"value":1551}," factory",{"type":39,"tag":48,"props":1553,"children":1554},{},[1555,1561,1562,1568,1570,1575],{"type":39,"tag":54,"props":1556,"children":1558},{"className":1557},[],[1559],{"type":45,"value":1560},"createLocalWorld()",{"type":45,"value":152},{"type":39,"tag":54,"props":1563,"children":1565},{"className":1564},[],[1566],{"type":45,"value":1567},"createVercelWorld()",{"type":45,"value":1569}," are gone from the first-party packages, which now export ",{"type":39,"tag":54,"props":1571,"children":1573},{"className":1572},[],[1574],{"type":45,"value":1549},{"type":45,"value":1576},". Match that shape. The arguments are unchanged; this is a rename.",{"type":39,"tag":1242,"props":1578,"children":1580},{"id":1579},"worlds-are-injected-at-build-time",[1581],{"type":45,"value":1582},"Worlds are injected at build time",{"type":39,"tag":48,"props":1584,"children":1585},{},[1586,1588,1594],{"type":45,"value":1587},"World selection is static, resolved into host bundles by the build rather than looked up dynamically at runtime. Verify the World still resolves after the upgrade and that its module graph survives bundling. A World that relied on a runtime ",{"type":39,"tag":54,"props":1589,"children":1591},{"className":1590},[],[1592],{"type":45,"value":1593},"require",{"type":45,"value":1595}," of a path computed from an environment variable will not be found.",{"type":39,"tag":104,"props":1597,"children":1599},{"id":1598},"step-4-the-contract-changes",[1600],{"type":45,"value":1601},"Step 4 — the contract changes",{"type":39,"tag":48,"props":1603,"children":1604},{},[1605],{"type":45,"value":1606},"These change no signature. A World ported by types alone compiles and then behaves incorrectly.",{"type":39,"tag":757,"props":1608,"children":1609},{},[1610,1636,1676,1720,1730],{"type":39,"tag":120,"props":1611,"children":1612},{},[1613,1618,1620,1626,1628,1634],{"type":39,"tag":96,"props":1614,"children":1615},{},[1616],{"type":45,"value":1617},"Suspension dispatch is batched.",{"type":45,"value":1619}," The asymmetric ",{"type":39,"tag":54,"props":1621,"children":1623},{"className":1622},[],[1624],{"type":45,"value":1625},"{ timeoutSeconds }",{"type":45,"value":1627}," wait-return contract is gone. A wait is an ordinary queue continuation carrying ",{"type":39,"tag":54,"props":1629,"children":1631},{"className":1630},[],[1632],{"type":45,"value":1633},"delaySeconds",{"type":45,"value":1635},", and a suspension dispatches its waits and its steps as one parallel batch. A queue that assumed one message per suspension needs to handle the batch.",{"type":39,"tag":120,"props":1637,"children":1638},{},[1639,1644,1646,1651,1653,1659,1660,1666,1668,1674],{"type":39,"tag":96,"props":1640,"children":1641},{},[1642],{"type":45,"value":1643},"Step queue topics are retired.",{"type":45,"value":1645}," The ",{"type":39,"tag":54,"props":1647,"children":1649},{"className":1648},[],[1650],{"type":45,"value":372},{"type":45,"value":1652}," queue kind no longer exists. Queued steps travel on the workflow topic carrying ",{"type":39,"tag":54,"props":1654,"children":1656},{"className":1655},[],[1657],{"type":45,"value":1658},"stepId",{"type":45,"value":152},{"type":39,"tag":54,"props":1661,"children":1663},{"className":1662},[],[1664],{"type":45,"value":1665},"stepName",{"type":45,"value":1667}," in the payload, and execute in the combined flow handler. Drop any ",{"type":39,"tag":54,"props":1669,"children":1671},{"className":1670},[],[1672],{"type":45,"value":1673},"__wkf_step_*",{"type":45,"value":1675}," topic provisioning.",{"type":39,"tag":120,"props":1677,"children":1678},{},[1679,1691,1693,1698,1700,1705,1706,1711,1713,1718],{"type":39,"tag":96,"props":1680,"children":1681},{},[1682,1684,1689],{"type":45,"value":1683},"The ",{"type":39,"tag":54,"props":1685,"children":1687},{"className":1686},[],[1688],{"type":45,"value":409},{"type":45,"value":1690}," capability is gone, and so is the reason for it.",{"type":45,"value":1692}," A World that rejected an event creation whose snapshot was behind the log can delete that code and its ",{"type":39,"tag":54,"props":1694,"children":1696},{"className":1695},[],[1697],{"type":45,"value":416},{"type":45,"value":1699}," \u002F ",{"type":39,"tag":54,"props":1701,"children":1703},{"className":1702},[],[1704],{"type":45,"value":423},{"type":45,"value":1699},{"type":39,"tag":54,"props":1707,"children":1709},{"className":1708},[],[1710],{"type":45,"value":430},{"type":45,"value":1712}," plumbing. Bump-and-report replaced it: a stale replay costs a merge instead of a rejection. ",{"type":39,"tag":54,"props":1714,"children":1716},{"className":1715},[],[1717],{"type":45,"value":402},{"type":45,"value":1719}," still exists for a World that allocates slots away from the commit and would rather refuse than report, which a World following step 1 is not.",{"type":39,"tag":120,"props":1721,"children":1722},{},[1723,1728],{"type":39,"tag":96,"props":1724,"children":1725},{},[1726],{"type":45,"value":1727},"Capabilities fail closed.",{"type":45,"value":1729}," An unadvertised capability costs performance, never correctness, so a partial World stays correct while it catches up. The reverse is not true: advertising something not enforced removes a guard the runtime was relying on. Set a flag only once the behavior is implemented.",{"type":39,"tag":120,"props":1731,"children":1732},{},[1733,1738,1739,1744,1746,1751,1752,1757,1758,1763,1765,1771,1773,1779,1781,1787,1789,1795,1797,1803],{"type":39,"tag":96,"props":1734,"children":1735},{},[1736],{"type":45,"value":1737},"Event creation may return a delta.",{"type":45,"value":805},{"type":39,"tag":54,"props":1740,"children":1742},{"className":1741},[],[1743],{"type":45,"value":811},{"type":45,"value":1745}," may return events alongside the one it created, in ",{"type":39,"tag":54,"props":1747,"children":1749},{"className":1748},[],[1750],{"type":45,"value":842},{"type":45,"value":1699},{"type":39,"tag":54,"props":1753,"children":1755},{"className":1754},[],[1756],{"type":45,"value":850},{"type":45,"value":1699},{"type":39,"tag":54,"props":1759,"children":1761},{"className":1760},[],[1762],{"type":45,"value":857},{"type":45,"value":1764},". Beyond the bump-and-report case in step 1, the runtime uses this to skip a follow-up ",{"type":39,"tag":54,"props":1766,"children":1768},{"className":1767},[],[1769],{"type":45,"value":1770},"events.list",{"type":45,"value":1772}," on ",{"type":39,"tag":54,"props":1774,"children":1776},{"className":1775},[],[1777],{"type":45,"value":1778},"run_started",{"type":45,"value":1780},", on step-terminal writes carrying ",{"type":39,"tag":54,"props":1782,"children":1784},{"className":1783},[],[1785],{"type":45,"value":1786},"sinceCursor",{"type":45,"value":1788},", and on ",{"type":39,"tag":54,"props":1790,"children":1792},{"className":1791},[],[1793],{"type":45,"value":1794},"hook_received",{"type":45,"value":1796}," writes carrying ",{"type":39,"tag":54,"props":1798,"children":1800},{"className":1799},[],[1801],{"type":45,"value":1802},"preloadEvents",{"type":45,"value":1804},". All three are advisory: returning only the created event stays correct and pays one more round trip.",{"type":39,"tag":104,"props":1806,"children":1808},{"id":1807},"step-5-optional-surface-worth-adopting",[1809],{"type":45,"value":1810},"Step 5 — optional surface worth adopting",{"type":39,"tag":48,"props":1812,"children":1813},{},[1814],{"type":45,"value":1815},"None of this is required, and the runtime routes around each absence. Report what the World is missing rather than implementing everything unprompted.",{"type":39,"tag":48,"props":1817,"children":1818},{},[1819,1824,1826,1832,1833,1839,1840,1846,1847,1853,1855,1860,1861,1867,1868,1874,1875,1881,1882,1888,1889,1895,1896,1902,1903,1909,1910,1916,1917,1923],{"type":39,"tag":54,"props":1820,"children":1822},{"className":1821},[],[1823],{"type":45,"value":292},{"type":45,"value":1825}," (",{"type":39,"tag":54,"props":1827,"children":1829},{"className":1828},[],[1830],{"type":45,"value":1831},"hookRetention.active",{"type":45,"value":137},{"type":39,"tag":54,"props":1834,"children":1836},{"className":1835},[],[1837],{"type":45,"value":1838},"hookResumeDedup",{"type":45,"value":137},{"type":39,"tag":54,"props":1841,"children":1843},{"className":1842},[],[1844],{"type":45,"value":1845},"deploymentAffinity",{"type":45,"value":137},{"type":39,"tag":54,"props":1848,"children":1850},{"className":1849},[],[1851],{"type":45,"value":1852},"maxConcurrency",{"type":45,"value":1854},"), ",{"type":39,"tag":54,"props":1856,"children":1858},{"className":1857},[],[1859],{"type":45,"value":299},{"type":45,"value":137},{"type":39,"tag":54,"props":1862,"children":1864},{"className":1863},[],[1865],{"type":45,"value":1866},"runs.experimentalSetAttributes",{"type":45,"value":137},{"type":39,"tag":54,"props":1869,"children":1871},{"className":1870},[],[1872],{"type":45,"value":1873},"runs.cancelMany",{"type":45,"value":137},{"type":39,"tag":54,"props":1876,"children":1878},{"className":1877},[],[1879],{"type":45,"value":1880},"getRuntimeDeadline()",{"type":45,"value":137},{"type":39,"tag":54,"props":1883,"children":1885},{"className":1884},[],[1886],{"type":45,"value":1887},"getEnvironment()",{"type":45,"value":137},{"type":39,"tag":54,"props":1890,"children":1892},{"className":1891},[],[1893],{"type":45,"value":1894},"createRunId()",{"type":45,"value":137},{"type":39,"tag":54,"props":1897,"children":1899},{"className":1898},[],[1900],{"type":45,"value":1901},"describeRun()",{"type":45,"value":137},{"type":39,"tag":54,"props":1904,"children":1906},{"className":1905},[],[1907],{"type":45,"value":1908},"getEncryptionKeyForRun()",{"type":45,"value":137},{"type":39,"tag":54,"props":1911,"children":1913},{"className":1912},[],[1914],{"type":45,"value":1915},"resolveLatestDeploymentId()",{"type":45,"value":137},{"type":39,"tag":54,"props":1918,"children":1920},{"className":1919},[],[1921],{"type":45,"value":1922},"close()",{"type":45,"value":357},{"type":39,"tag":48,"props":1925,"children":1926},{},[1927,1929,1934,1936,1941,1943,1949],{"type":45,"value":1928},"Two are worth raising unprompted because their absence is felt rather than reported. Without ",{"type":39,"tag":54,"props":1930,"children":1932},{"className":1931},[],[1933],{"type":45,"value":1880},{"type":45,"value":1935}," the inline replay budget is a flat two minutes, so a host with a long function timeout does less work per invocation than it could. Without ",{"type":39,"tag":54,"props":1937,"children":1939},{"className":1938},[],[1940],{"type":45,"value":1922},{"type":45,"value":1942},", CLI commands and short-lived processes cannot exit cleanly without ",{"type":39,"tag":54,"props":1944,"children":1946},{"className":1945},[],[1947],{"type":45,"value":1948},"process.exit()",{"type":45,"value":357},{"type":39,"tag":104,"props":1951,"children":1953},{"id":1952},"step-6-the-rollout",[1954],{"type":45,"value":1955},"Step 6 — the rollout",{"type":39,"tag":48,"props":1957,"children":1958},{},[1959],{"type":45,"value":1960},"Warn the user, in the migration report, before they deploy:",{"type":39,"tag":48,"props":1962,"children":1963},{},[1964,1969],{"type":39,"tag":96,"props":1965,"children":1966},{},[1967],{"type":45,"value":1968},"Runs already in the store cannot be replayed by the new code.",{"type":45,"value":1970}," A ULID-numbered run is not readable as positions, and the runtime refuses it rather than guessing. There is no mixed-scheme mode and no per-run fallback.",{"type":39,"tag":48,"props":1972,"children":1973},{},[1974],{"type":45,"value":1975},"Which follows depends on intake item 9. Where a run executes on the deployment that created it, this resolves itself: those runs finish on the build that started them and never meet the new code. Where a single deployment serves every run, the in-flight ones must be drained on the 4.x build before the v5 World is deployed, or they will fail.",{"type":39,"tag":104,"props":1977,"children":1979},{"id":1978},"verification",[1980],{"type":45,"value":1981},"Verification",{"type":39,"tag":48,"props":1983,"children":1984},{},[1985],{"type":45,"value":1986},"Wire up the conformance suite first. It is the cheapest way to catch the step 1 work being wrong:",{"type":39,"tag":512,"props":1988,"children":1990},{"className":514,"code":1989,"language":516,"meta":517,"style":517},"import { createTestSuite } from '@workflow\u002Fworld-testing';\n\ncreateTestSuite('@my-org\u002Fmy-world'); \u002F\u002F or a path to the built entrypoint\n",[1991],{"type":39,"tag":54,"props":1992,"children":1993},{"__ignoreMap":517},[1994,2034,2041],{"type":39,"tag":523,"props":1995,"children":1996},{"class":525,"line":526},[1997,2001,2005,2010,2014,2018,2022,2026,2030],{"type":39,"tag":523,"props":1998,"children":1999},{"style":530},[2000],{"type":45,"value":533},{"type":39,"tag":523,"props":2002,"children":2003},{"style":536},[2004],{"type":45,"value":539},{"type":39,"tag":523,"props":2006,"children":2007},{"style":542},[2008],{"type":45,"value":2009}," createTestSuite",{"type":39,"tag":523,"props":2011,"children":2012},{"style":536},[2013],{"type":45,"value":569},{"type":39,"tag":523,"props":2015,"children":2016},{"style":530},[2017],{"type":45,"value":574},{"type":39,"tag":523,"props":2019,"children":2020},{"style":536},[2021],{"type":45,"value":579},{"type":39,"tag":523,"props":2023,"children":2024},{"style":582},[2025],{"type":45,"value":453},{"type":39,"tag":523,"props":2027,"children":2028},{"style":536},[2029],{"type":45,"value":589},{"type":39,"tag":523,"props":2031,"children":2032},{"style":536},[2033],{"type":45,"value":594},{"type":39,"tag":523,"props":2035,"children":2036},{"class":525,"line":597},[2037],{"type":39,"tag":523,"props":2038,"children":2039},{"emptyLinePlaceholder":601},[2040],{"type":45,"value":604},{"type":39,"tag":523,"props":2042,"children":2043},{"class":525,"line":607},[2044,2048,2052,2056,2061,2065,2069,2073],{"type":39,"tag":523,"props":2045,"children":2046},{"style":611},[2047],{"type":45,"value":460},{"type":39,"tag":523,"props":2049,"children":2050},{"style":542},[2051],{"type":45,"value":619},{"type":39,"tag":523,"props":2053,"children":2054},{"style":536},[2055],{"type":45,"value":589},{"type":39,"tag":523,"props":2057,"children":2058},{"style":582},[2059],{"type":45,"value":2060},"@my-org\u002Fmy-world",{"type":39,"tag":523,"props":2062,"children":2063},{"style":536},[2064],{"type":45,"value":589},{"type":39,"tag":523,"props":2066,"children":2067},{"style":542},[2068],{"type":45,"value":630},{"type":39,"tag":523,"props":2070,"children":2071},{"style":536},[2072],{"type":45,"value":635},{"type":39,"tag":523,"props":2074,"children":2075},{"style":638},[2076],{"type":45,"value":2077}," \u002F\u002F or a path to the built entrypoint\n",{"type":39,"tag":48,"props":2079,"children":2080},{},[2081,2083,2089,2091,2097],{"type":45,"value":2082},"The suite spawns a server with ",{"type":39,"tag":54,"props":2084,"children":2086},{"className":2085},[],[2087],{"type":45,"value":2088},"WORKFLOW_TARGET_WORLD",{"type":45,"value":2090}," set to that value and runs real workflows against it. Its ",{"type":39,"tag":54,"props":2092,"children":2094},{"className":2093},[],[2095],{"type":45,"value":2096},"numbers events by position",{"type":45,"value":2098}," test fails a World whose IDs do not decode to slots, whose run is not dense from 1, or whose IDs are not in canonical form. That turns the failure that would otherwise appear on a first replay into one line of test output.",{"type":39,"tag":48,"props":2100,"children":2101},{},[2102],{"type":45,"value":2103},"Then run, in order, and report actual output:",{"type":39,"tag":116,"props":2105,"children":2106},{},[2107,2112,2117,2122],{"type":39,"tag":120,"props":2108,"children":2109},{},[2110],{"type":45,"value":2111},"Build and typecheck the World package.",{"type":39,"tag":120,"props":2113,"children":2114},{},[2115],{"type":45,"value":2116},"The conformance suite.",{"type":39,"tag":120,"props":2118,"children":2119},{},[2120],{"type":45,"value":2121},"The World's own test suite.",{"type":39,"tag":120,"props":2123,"children":2124},{},[2125,2127,2132],{"type":45,"value":2126},"An end-to-end run against a real app, covering: a run that suspends on a step and one that suspends on a wait; a parallel fan-out, so concurrent ",{"type":39,"tag":54,"props":2128,"children":2130},{"className":2129},[],[2131],{"type":45,"value":811},{"type":45,"value":2133}," calls race for the same slot; a hook resumed after its run has progressed; a stream written and read back, including one closed before the reader attaches.",{"type":39,"tag":48,"props":2135,"children":2136},{},[2137,2139,2144],{"type":45,"value":2138},"Concurrency is the part that light testing misses. If the World's tests never issue two ",{"type":39,"tag":54,"props":2140,"children":2142},{"className":2141},[],[2143],{"type":45,"value":811},{"type":45,"value":2145}," calls for the same run at once, add one that does before calling the migration done.",{"type":39,"tag":48,"props":2147,"children":2148},{},[2149],{"type":45,"value":2150},"Fail the migration if any of these are true:",{"type":39,"tag":757,"props":2152,"children":2155},{"className":2153},[2154],"contains-task-list",[2156,2173,2182,2191,2213,2239,2255,2283,2300,2324,2333,2348,2357],{"type":39,"tag":120,"props":2157,"children":2160},{"className":2158},[2159],"task-list-item",[2161,2166,2168],{"type":39,"tag":2162,"props":2163,"children":2165},"input",{"disabled":601,"type":2164},"checkbox",[],{"type":45,"value":2167}," an event ID is produced by anything other than ",{"type":39,"tag":54,"props":2169,"children":2171},{"className":2170},[],[2172],{"type":45,"value":729},{"type":39,"tag":120,"props":2174,"children":2176},{"className":2175},[2159],[2177,2180],{"type":39,"tag":2162,"props":2178,"children":2179},{"disabled":601,"type":2164},[],{"type":45,"value":2181}," the slot is chosen by reading a maximum, or a counter, outside the operation that commits the event",{"type":39,"tag":120,"props":2183,"children":2185},{"className":2184},[2159],[2186,2189],{"type":39,"tag":2162,"props":2187,"children":2188},{"disabled":601,"type":2164},[],{"type":45,"value":2190}," the slot is handed out before the commit",{"type":39,"tag":120,"props":2192,"children":2194},{"className":2193},[2159],[2195,2198,2199,2204,2206,2211],{"type":39,"tag":2162,"props":2196,"children":2197},{"disabled":601,"type":2164},[],{"type":45,"value":805},{"type":39,"tag":54,"props":2200,"children":2202},{"className":2201},[],[2203],{"type":45,"value":811},{"type":45,"value":2205}," rejects, throws or retries a write whose ",{"type":39,"tag":54,"props":2207,"children":2209},{"className":2208},[],[2210],{"type":45,"value":827},{"type":45,"value":2212}," slot was taken, instead of bumping to the next free slot",{"type":39,"tag":120,"props":2214,"children":2216},{"className":2215},[2159],[2217,2220,2222,2227,2228,2233,2234],{"type":39,"tag":2162,"props":2218,"children":2219},{"disabled":601,"type":2164},[],{"type":45,"value":2221}," a bumped write returns without the skipped events on ",{"type":39,"tag":54,"props":2223,"children":2225},{"className":2224},[],[2226],{"type":45,"value":842},{"type":45,"value":1699},{"type":39,"tag":54,"props":2229,"children":2231},{"className":2230},[],[2232],{"type":45,"value":850},{"type":45,"value":1699},{"type":39,"tag":54,"props":2235,"children":2237},{"className":2236},[],[2238],{"type":45,"value":857},{"type":39,"tag":120,"props":2240,"children":2242},{"className":2241},[2159],[2243,2246,2248,2253],{"type":39,"tag":2162,"props":2244,"children":2245},{"disabled":601,"type":2164},[],{"type":45,"value":2247}," a create carrying no ",{"type":39,"tag":54,"props":2249,"children":2251},{"className":2250},[],[2252],{"type":45,"value":819},{"type":45,"value":2254}," is rejected",{"type":39,"tag":120,"props":2256,"children":2258},{"className":2257},[2159],[2259,2262,2263,2268,2270,2275,2277],{"type":39,"tag":2162,"props":2260,"children":2261},{"disabled":601,"type":2164},[],{"type":45,"value":805},{"type":39,"tag":54,"props":2264,"children":2266},{"className":2265},[],[2267],{"type":45,"value":267},{"type":45,"value":2269}," is a literal, or ",{"type":39,"tag":54,"props":2271,"children":2273},{"className":2272},[],[2274],{"type":45,"value":1222},{"type":45,"value":2276},", rather than ",{"type":39,"tag":54,"props":2278,"children":2280},{"className":2279},[],[2281],{"type":45,"value":2282},"SPEC_VERSION_CURRENT",{"type":39,"tag":120,"props":2284,"children":2286},{"className":2285},[2159],[2287,2290,2292,2298],{"type":39,"tag":2162,"props":2288,"children":2289},{"disabled":601,"type":2164},[],{"type":45,"value":2291}," a ",{"type":39,"tag":54,"props":2293,"children":2295},{"className":2294},[],[2296],{"type":45,"value":2297},"streams.*",{"type":45,"value":2299}," call kept the v4 argument order (name before runId)",{"type":39,"tag":120,"props":2301,"children":2303},{"className":2302},[2159],[2304,2307,2308,2314,2316,2322],{"type":39,"tag":2162,"props":2305,"children":2306},{"disabled":601,"type":2164},[],{"type":45,"value":805},{"type":39,"tag":54,"props":2309,"children":2311},{"className":2310},[],[2312],{"type":45,"value":2313},"steps.get",{"type":45,"value":2315}," or ",{"type":39,"tag":54,"props":2317,"children":2319},{"className":2318},[],[2320],{"type":45,"value":2321},"listByCorrelationId",{"type":45,"value":2323}," is reachable without a run ID",{"type":39,"tag":120,"props":2325,"children":2327},{"className":2326},[2159],[2328,2331],{"type":39,"tag":2162,"props":2329,"children":2330},{"disabled":601,"type":2164},[],{"type":45,"value":2332}," a capability is advertised whose behavior is not implemented",{"type":39,"tag":120,"props":2334,"children":2336},{"className":2335},[2159],[2337,2340,2341,2346],{"type":39,"tag":2162,"props":2338,"children":2339},{"disabled":601,"type":2164},[],{"type":45,"value":805},{"type":39,"tag":54,"props":2342,"children":2344},{"className":2343},[],[2345],{"type":45,"value":453},{"type":45,"value":2347}," is not wired up, or its results were not reported",{"type":39,"tag":120,"props":2349,"children":2351},{"className":2350},[2159],[2352,2355],{"type":39,"tag":2162,"props":2353,"children":2354},{"disabled":601,"type":2164},[],{"type":45,"value":2356}," the rollout warning in step 6 was not given",{"type":39,"tag":120,"props":2358,"children":2360},{"className":2359},[2159],[2361,2364],{"type":39,"tag":2162,"props":2362,"children":2363},{"disabled":601,"type":2164},[],{"type":45,"value":2365}," the build, typecheck or test results were not actually run and reported",{"type":39,"tag":104,"props":2367,"children":2369},{"id":2368},"required-output-shape",[2370],{"type":45,"value":2371},"Required output shape",{"type":39,"tag":512,"props":2373,"children":2377},{"className":2374,"code":2375,"language":2376,"meta":517,"style":517},"language-md shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","## Summary\n## Event ID Allocation\n## Interface Changes\n## Contract Changes\n## Optional Surface Not Implemented\n## Rollout\n## Verification\n## Open Questions\n","md",[2378],{"type":39,"tag":54,"props":2379,"children":2380},{"__ignoreMap":517},[2381,2394,2406,2418,2430,2442,2454,2466],{"type":39,"tag":523,"props":2382,"children":2383},{"class":525,"line":526},[2384,2389],{"type":39,"tag":523,"props":2385,"children":2386},{"style":536},[2387],{"type":45,"value":2388},"## ",{"type":39,"tag":523,"props":2390,"children":2391},{"style":1131},[2392],{"type":45,"value":2393},"Summary\n",{"type":39,"tag":523,"props":2395,"children":2396},{"class":525,"line":597},[2397,2401],{"type":39,"tag":523,"props":2398,"children":2399},{"style":536},[2400],{"type":45,"value":2388},{"type":39,"tag":523,"props":2402,"children":2403},{"style":1131},[2404],{"type":45,"value":2405},"Event ID Allocation\n",{"type":39,"tag":523,"props":2407,"children":2408},{"class":525,"line":607},[2409,2413],{"type":39,"tag":523,"props":2410,"children":2411},{"style":536},[2412],{"type":45,"value":2388},{"type":39,"tag":523,"props":2414,"children":2415},{"style":1131},[2416],{"type":45,"value":2417},"Interface Changes\n",{"type":39,"tag":523,"props":2419,"children":2420},{"class":525,"line":644},[2421,2425],{"type":39,"tag":523,"props":2422,"children":2423},{"style":536},[2424],{"type":45,"value":2388},{"type":39,"tag":523,"props":2426,"children":2427},{"style":1131},[2428],{"type":45,"value":2429},"Contract Changes\n",{"type":39,"tag":523,"props":2431,"children":2432},{"class":525,"line":683},[2433,2437],{"type":39,"tag":523,"props":2434,"children":2435},{"style":536},[2436],{"type":45,"value":2388},{"type":39,"tag":523,"props":2438,"children":2439},{"style":1131},[2440],{"type":45,"value":2441},"Optional Surface Not Implemented\n",{"type":39,"tag":523,"props":2443,"children":2444},{"class":525,"line":956},[2445,2449],{"type":39,"tag":523,"props":2446,"children":2447},{"style":536},[2448],{"type":45,"value":2388},{"type":39,"tag":523,"props":2450,"children":2451},{"style":1131},[2452],{"type":45,"value":2453},"Rollout\n",{"type":39,"tag":523,"props":2455,"children":2456},{"class":525,"line":965},[2457,2461],{"type":39,"tag":523,"props":2458,"children":2459},{"style":536},[2460],{"type":45,"value":2388},{"type":39,"tag":523,"props":2462,"children":2463},{"style":1131},[2464],{"type":45,"value":2465},"Verification\n",{"type":39,"tag":523,"props":2467,"children":2468},{"class":525,"line":974},[2469,2473],{"type":39,"tag":523,"props":2470,"children":2471},{"style":536},[2472],{"type":45,"value":2388},{"type":39,"tag":523,"props":2474,"children":2475},{"style":1131},[2476],{"type":45,"value":2477},"Open Questions\n",{"type":39,"tag":757,"props":2479,"children":2480},{},[2481,2492,2503],{"type":39,"tag":120,"props":2482,"children":2483},{},[2484,2490],{"type":39,"tag":54,"props":2485,"children":2487},{"className":2486},[],[2488],{"type":45,"value":2489},"## Event ID Allocation",{"type":45,"value":2491}," states where the slot is now computed, what settles a race for it, and how a bumped write reports the span it skipped. Quote the code.",{"type":39,"tag":120,"props":2493,"children":2494},{},[2495,2501],{"type":39,"tag":54,"props":2496,"children":2498},{"className":2497},[],[2499],{"type":45,"value":2500},"## Optional Surface Not Implemented",{"type":45,"value":2502}," lists what was left out and what each absence costs, so the user can decide.",{"type":39,"tag":120,"props":2504,"children":2505},{},[2506,2512],{"type":39,"tag":54,"props":2507,"children":2509},{"className":2508},[],[2510],{"type":45,"value":2511},"## Rollout",{"type":45,"value":2513}," carries the step 6 warning and which of its two cases applies to this deployment.",{"type":39,"tag":104,"props":2515,"children":2517},{"id":2516},"reference",[2518],{"type":45,"value":2519},"Reference",{"type":39,"tag":757,"props":2521,"children":2522},{},[2523,2536,2547,2558,2569],{"type":39,"tag":120,"props":2524,"children":2525},{},[2526,2528],{"type":45,"value":2527},"Upgrading a World to v5: ",{"type":39,"tag":2529,"props":2530,"children":2534},"a",{"href":2531,"rel":2532},"https:\u002F\u002Fworkflow.dev\u002Fworlds\u002Fupgrading-to-v5",[2533],"nofollow",[2535],{"type":45,"value":2531},{"type":39,"tag":120,"props":2537,"children":2538},{},[2539,2541],{"type":45,"value":2540},"Building a World: ",{"type":39,"tag":2529,"props":2542,"children":2545},{"href":2543,"rel":2544},"https:\u002F\u002Fworkflow.dev\u002Fworlds\u002Fbuilding-a-world",[2533],[2546],{"type":45,"value":2543},{"type":39,"tag":120,"props":2548,"children":2549},{},[2550,2552],{"type":45,"value":2551},"Event IDs: ",{"type":39,"tag":2529,"props":2553,"children":2556},{"href":2554,"rel":2555},"https:\u002F\u002Fworkflow.dev\u002Fdocs\u002Fhow-it-works\u002Fevent-sourcing#event-ids",[2533],[2557],{"type":45,"value":2554},{"type":39,"tag":120,"props":2559,"children":2560},{},[2561,2563],{"type":45,"value":2562},"What's new in v5 (application-facing): ",{"type":39,"tag":2529,"props":2564,"children":2567},{"href":2565,"rel":2566},"https:\u002F\u002Fworkflow.dev\u002Fdocs\u002Fwhats-new",[2533],[2568],{"type":45,"value":2565},{"type":39,"tag":120,"props":2570,"children":2571},{},[2572,2574,2580,2581,2587,2589],{"type":45,"value":2573},"Reference implementations: ",{"type":39,"tag":54,"props":2575,"children":2577},{"className":2576},[],[2578],{"type":45,"value":2579},"packages\u002Fworld-local",{"type":45,"value":152},{"type":39,"tag":54,"props":2582,"children":2584},{"className":2583},[],[2585],{"type":45,"value":2586},"packages\u002Fworld-postgres",{"type":45,"value":2588}," in ",{"type":39,"tag":2529,"props":2590,"children":2592},{"href":20,"rel":2591},[2533],[2593],{"type":45,"value":20},{"type":39,"tag":2595,"props":2596,"children":2597},"style",{},[2598],{"type":45,"value":2599},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2601,"total":2778},[2602,2622,2636,2655,2666,2681,2697,2715,2727,2746,2758,2768],{"slug":2603,"name":2603,"fn":2604,"description":2605,"org":2606,"tags":2607,"stars":2619,"repoUrl":2620,"updatedAt":2621},"next-cache-components-adoption","enable and migrate to Next.js Cache Components","Turn on Cache Components in a Next.js app and resolve the blocking routes it surfaces. Use when the user wants to enable, adopt, or migrate to Cache Components, flip the `cacheComponents` flag, work through a flood of blocking-prerender \u002F instant validation errors, run the `cache-components-instant-false` codemod, or decide between opting routes out with `export const instant = false` and fixing them in place.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2608,2611,2614,2615,2618],{"name":2609,"slug":2610,"type":15},"Caching","caching",{"name":2612,"slug":2613,"type":15},"Frontend","frontend",{"name":17,"slug":18,"type":15},{"name":2616,"slug":2617,"type":15},"Next.js","next-js",{"name":9,"slug":8,"type":15},141208,"https:\u002F\u002Fgithub.com\u002Fvercel\u002Fnext.js","2026-08-22T03:25:50.984703",{"slug":2623,"name":2623,"fn":2624,"description":2625,"org":2626,"tags":2627,"stars":2619,"repoUrl":2620,"updatedAt":2635},"next-cache-components-optimizer","optimize Next.js cache components","Drive a Next.js route to instant navigation by setting up an agentic loop, under Cache Components \u002F PPR, on initial load (hard navigation) and client-side navigation (soft navigation). Encode the goal as a failing @next\u002Fplaywright instant() e2e and work it to green, one verified route at a time; the shipped test then guards against regression. Use when asked to make a route's navigation instant (its static shell commits immediately), fix a route whose static shell isn't prerendered\u002Fserved\u002Fprefetched, grow a route's static shell or fix its slow first paint, diagnose which Suspense boundary keeps a route out of its static shell, or write the instant() e2e guard for one. Requires Next.js 16.3+ with cacheComponents; directs an upgrade if older.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2628,2629,2630,2631,2634],{"name":2609,"slug":2610,"type":15},{"name":2612,"slug":2613,"type":15},{"name":2616,"slug":2617,"type":15},{"name":2632,"slug":2633,"type":15},"Performance","performance",{"name":9,"slug":8,"type":15},"2026-08-12T04:28:44.716229",{"slug":2637,"name":2637,"fn":2638,"description":2639,"org":2640,"tags":2641,"stars":2619,"repoUrl":2620,"updatedAt":2654},"next-dev-loop","verify Next.js runtime behavior","Verify Next.js runtime behavior after editing app code. Use this skill to confirm a change actually works in a running app — not just that it compiles or type-checks. Combines \u002F_next\u002Fmcp (Next.js's view) with agent-browser (the browser's view). Requires a running `next dev`.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2642,2645,2646,2649,2650,2651],{"name":2643,"slug":2644,"type":15},"Debugging","debugging",{"name":2612,"slug":2613,"type":15},{"name":2647,"slug":2648,"type":15},"Local Development","local-development",{"name":2616,"slug":2617,"type":15},{"name":9,"slug":8,"type":15},{"name":2652,"slug":2653,"type":15},"Web Development","web-development","2026-08-22T03:25:48.935638",{"slug":2656,"name":2656,"fn":2657,"description":2658,"org":2659,"tags":2660,"stars":2619,"repoUrl":2620,"updatedAt":2665},"next-partial-prefetching-adoption","adopt Partial Prefetching in Next.js apps","Turn on Partial Prefetching in a Next.js app and work through the insights it surfaces. Use when the user wants to enable or adopt Partial Prefetching, flip the `partialPrefetching` flag, opt routes in with `export const prefetch = 'partial'`, audit `\u003CLink prefetch={true}>` calls, or resolve the instant-link-prefetch-partial and instant-shell-url-data insights.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2661,2662,2663,2664],{"name":2612,"slug":2613,"type":15},{"name":2616,"slug":2617,"type":15},{"name":2632,"slug":2633,"type":15},{"name":9,"slug":8,"type":15},"2026-08-22T03:25:49.953467",{"slug":2667,"name":2667,"fn":2668,"description":2669,"org":2670,"tags":2671,"stars":2678,"repoUrl":2679,"updatedAt":2680},"turborepo","manage monorepos with Turborepo","Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines,\ndependsOn, caching, remote cache, the \"turbo\" CLI, --filter, --affected, CI optimization, environment\nvariables, internal packages, monorepo structure\u002Fbest practices, and boundaries.\n\nUse when user: configures tasks\u002Fworkflows\u002Fpipelines, creates packages, sets up\nmonorepo, shares code between apps, runs changed\u002Faffected packages, debugs cache,\nor has apps\u002Fpackages directories.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2672,2675,2676],{"name":2673,"slug":2674,"type":15},"CI\u002FCD","ci-cd",{"name":2632,"slug":2633,"type":15},{"name":2677,"slug":2667,"type":15},"Turborepo",30809,"https:\u002F\u002Fgithub.com\u002Fvercel\u002Fturborepo","2026-08-19T03:29:36.331344",{"slug":2682,"name":2682,"fn":2683,"description":2684,"org":2685,"tags":2686,"stars":2694,"repoUrl":2695,"updatedAt":2696},"add-function-examples","add AI function examples for testing","Guide for adding new AI function examples, for testing specific features against the actual provider APIs.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2687,2690,2693],{"name":2688,"slug":2689,"type":15},"AI SDK","ai-sdk",{"name":2691,"slug":2692,"type":15},"Testing","testing",{"name":9,"slug":8,"type":15},25670,"https:\u002F\u002Fgithub.com\u002Fvercel\u002Fai","2026-04-06T18:55:51.318866",{"slug":2698,"name":2698,"fn":2699,"description":2700,"org":2701,"tags":2702,"stars":2694,"repoUrl":2695,"updatedAt":2714},"add-harness-package","add AI SDK harness packages","Guide for adding new AI SDK harness packages. Use when creating a new @ai-sdk\u002Fharness-\u003Cname> package that adapts a coding-agent runtime to HarnessV1.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2703,2706,2707,2710,2713],{"name":2704,"slug":2705,"type":15},"Agents","agents",{"name":2688,"slug":2689,"type":15},{"name":2708,"slug":2709,"type":15},"Harness","harness",{"name":2711,"slug":2712,"type":15},"SDK","sdk",{"name":9,"slug":8,"type":15},"2026-06-18T08:29:19.858737",{"slug":2716,"name":2716,"fn":2717,"description":2718,"org":2719,"tags":2720,"stars":2694,"repoUrl":2695,"updatedAt":2726},"add-provider-package","add new provider packages to AI SDK","Guide for adding new AI provider packages to the AI SDK. Use when creating a new @ai-sdk\u002F\u003Cprovider> package to integrate an AI service into the SDK.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2721,2722,2725],{"name":2688,"slug":2689,"type":15},{"name":2723,"slug":2724,"type":15},"API Development","api-development",{"name":9,"slug":8,"type":15},"2026-04-06T18:55:47.45549",{"slug":2728,"name":2728,"fn":2729,"description":2730,"org":2731,"tags":2732,"stars":2694,"repoUrl":2695,"updatedAt":2745},"adr-skill","create and maintain architecture decision records","Create and maintain Architecture Decision Records (ADRs) optimized for agentic coding workflows. Use when you need to propose, write, update, accept\u002Freject, deprecate, or supersede an ADR; bootstrap an adr folder and index; consult existing ADRs before implementing changes; or enforce ADR conventions. This skill uses Socratic questioning to capture intent before drafting, and validates output against an agent-readiness checklist.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2733,2736,2739,2742],{"name":2734,"slug":2735,"type":15},"ADR","adr",{"name":2737,"slug":2738,"type":15},"Architecture","architecture",{"name":2740,"slug":2741,"type":15},"Documentation","documentation",{"name":2743,"slug":2744,"type":15},"Engineering","engineering","2026-04-06T18:55:50.043694",{"slug":2689,"name":2689,"fn":2747,"description":2748,"org":2749,"tags":2750,"stars":2694,"repoUrl":2695,"updatedAt":2757},"build AI features with Vercel AI SDK","Answer questions about the AI SDK and help build AI-powered features. Use when developers: (1) Ask about AI SDK functions like generateText, streamText, ToolLoopAgent, embed, or tools, (2) Want to build AI agents, chatbots, RAG systems, or text generation features, (3) Have questions about AI providers (OpenAI, Anthropic, Google, etc.), streaming, tool calling, structured output, or embeddings, (4) Use React hooks like useChat or useCompletion. Triggers on: \"AI SDK\", \"Vercel AI SDK\", \"generateText\", \"streamText\", \"add AI to my app\", \"build an agent\", \"tool calling\", \"structured output\", \"useChat\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2751,2752,2753,2756],{"name":2704,"slug":2705,"type":15},{"name":2688,"slug":2689,"type":15},{"name":2754,"slug":2755,"type":15},"LLM","llm",{"name":9,"slug":8,"type":15},"2026-04-06T18:55:48.739463",{"slug":2759,"name":2759,"fn":2760,"description":2761,"org":2762,"tags":2763,"stars":2694,"repoUrl":2695,"updatedAt":2767},"capture-api-response-test-fixture","capture API response test fixtures","Capture API response test fixture.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2764,2765,2766],{"name":2723,"slug":2724,"type":15},{"name":2691,"slug":2692,"type":15},{"name":9,"slug":8,"type":15},"2026-04-06T18:55:56.374433",{"slug":2769,"name":2769,"fn":2770,"description":2771,"org":2772,"tags":2773,"stars":2694,"repoUrl":2695,"updatedAt":2777},"develop-ai-functions-example","develop AI SDK function examples","Develop examples for AI SDK functions. Use when creating, running, or modifying examples under examples\u002Fai-functions\u002Fsrc to validate provider support, demonstrate features, or create test fixtures.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2774,2775,2776],{"name":2688,"slug":2689,"type":15},{"name":2691,"slug":2692,"type":15},{"name":9,"slug":8,"type":15},"2026-04-06T18:55:55.088956",73,{"items":2780,"total":683},[2781,2794,2806,2811,2823],{"slug":2782,"name":2782,"fn":2783,"description":2784,"org":2785,"tags":2786,"stars":19,"repoUrl":20,"updatedAt":2793},"internal-dev-workbench","set up isolated development workbenches","Spin up a portless + tmux dev session for the Workflow SDK that gives each git worktree isolated `\u003Cbranch>.\u003Cname>.localhost` URLs for the Next.js workbench and the observability UI, plus a Claude statusline that surfaces those URLs. Use only when the user asks for a \"portless dev session\", a \"tmux dev layout for workflow\", \"worktree-isolated dev URLs\", or wants to wire workflow dev URLs into the Claude statusline. Do not activate for the generic \"start the dev server\" \u002F \"run pnpm dev\" task.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2787,2788,2789,2792],{"name":2743,"slug":2744,"type":15},{"name":2647,"slug":2648,"type":15},{"name":2790,"slug":2791,"type":15},"Observability","observability",{"name":9,"slug":8,"type":15},"2026-08-22T03:26:08.945287",{"slug":2795,"name":2795,"fn":2796,"description":2797,"org":2798,"tags":2799,"stars":19,"repoUrl":20,"updatedAt":2805},"migrating-to-workflow-sdk","migrate workflows to Vercel 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 \u002F wait.forToken \u002F triggerAndWait, ASL JSON state machines, Task\u002FChoice\u002FWait\u002FParallel states, task tokens, or child workflows.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2800,2801,2802],{"name":17,"slug":18,"type":15},{"name":9,"slug":8,"type":15},{"name":2803,"slug":2804,"type":15},"Workflow Automation","workflow-automation","2026-08-22T03:26:07.955862",{"slug":4,"name":4,"fn":5,"description":6,"org":2807,"tags":2808,"stars":19,"repoUrl":20,"updatedAt":21},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2809,2810],{"name":17,"slug":18,"type":15},{"name":13,"slug":14,"type":15},{"slug":14,"name":14,"fn":2812,"description":2813,"org":2814,"tags":2815,"stars":19,"repoUrl":20,"updatedAt":2822},"build durable workflows with Vercel","Creates durable, resumable workflows using Vercel's Workflow SDK. Use when building workflows that need to survive restarts, pause for external events, retry on failure, or coordinate multi-step operations over time. Triggers on mentions of \"workflow\", \"durable functions\", \"resumable\", \"workflow sdk\", \"queue\", \"event\", \"push\", \"subscribe\", or step-based orchestration.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2816,2819,2820,2821],{"name":2817,"slug":2818,"type":15},"Backend","backend",{"name":2711,"slug":2712,"type":15},{"name":9,"slug":8,"type":15},{"name":2803,"slug":2804,"type":15},"2026-08-22T03:26:07.009824",{"slug":2824,"name":2824,"fn":2825,"description":2826,"org":2827,"tags":2828,"stars":19,"repoUrl":20,"updatedAt":2835},"workflow-init","initialize Vercel Workflow SDK","Install and configure Vercel Workflow SDK before it exists in node_modules. Use when the user asks to \"install workflow\", \"set up workflow\", \"add durable workflows\", \"configure workflow sdk\", or \"init workflow\" for Next.js, Express, Hono, Fastify, NestJS, Nitro, Nuxt, Astro, SvelteKit, or Vite.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[2829,2832,2833,2834],{"name":2830,"slug":2831,"type":15},"Node.js","node-js",{"name":2711,"slug":2712,"type":15},{"name":9,"slug":8,"type":15},{"name":2803,"slug":2804,"type":15},"2026-08-22T03:26:09.958394"]