Commit Graph

153 Commits

Author SHA1 Message Date
Peter Wielander 01bbe66d5a [world] Add stream pagination and metadata endpoints (#1470) 2026-03-23 17:39:39 -07:00
Pranay Prakash 2ef33d2828 feat: export semantic error types and add API reference docs (#1447)
* feat: export semantic error types and add API reference documentation

Add missing error exports (HookNotFoundError, EntityConflictError,
RunExpiredError, TooEarlyError, ThrottleError, RunNotSupportedError,
WorkflowWorldError) to workflow/internal/errors. Create new error
classes for world-level semantics. Tighten TSDoc comments on all
error classes. Add API reference docs for all error types.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use @setup declarations, workflow/errors import, and errors/ doc section

- Replace @skip-typecheck with proper `declare` + `// @setup` lines
  so code samples are typechecked but setup lines hidden from readers
- Add `workflow/errors` export to package.json (public API, replaces
  `workflow/internal/errors` in docs)
- Add `workflow/errors` path mapping in docs-typecheck type-checker
- Add HookConflictError to re-export list
- Move all error docs under api-reference/workflow/errors/ subdirectory
- Update all internal cross-references and links

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: move error docs to top-level workflow-errors section

- Move semantic error docs to api-reference/workflow-errors/ (matching
  the workflow/errors import path, like workflow-api for workflow/api)
- Keep FatalError and RetryableError in api-reference/workflow/ since
  they're imported from workflow, not workflow/errors
- Fix all cross-reference links

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: update HTTP debug logger JSDoc to clarify scope

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: make TooEarlyError.retryAfter a number (seconds) matching WorkflowWorldError

TooEarlyError.retryAfter is now seconds (number) instead of a Date,
consistent with ThrottleError and WorkflowWorldError. The conversion
from seconds to Date is done at the consumer site (step-handler) rather
than at construction time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: address review feedback on docs accuracy

- WorkflowWorldError docs: add status, code, url, retryAfter properties
  to TSDoc; clarify that .is() only matches direct instances (not
  subclasses); use instanceof in catch-all example
- TooEarlyError/ThrottleError docs: mark retryAfter as optional (?)
  to match actual type definitions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 00:01:36 +00:00
James Berry 5502438bac [world-postgres] Migrate client from postgres.js to pg (#1484) 2026-03-23 16:30:27 -07:00
Peter Wielander 78f1b0e19f [core] Support negative startIndex for streaming (#1460) 2026-03-20 13:29:42 -07:00
Pranay Prakash aee035f944 refactor: replace HTTP status code checks with semantic error types (#1342)
* feat: classify run failure error codes and improve error logging

- Add RUN_ERROR_CODES (USER_ERROR, RUNTIME_ERROR) to @workflow/errors
- Populate errorCode in run_failed events via classifyRunError()
- Update web UI StatusBadge to show amber dot for infrastructure errors
- Improve world-local queue error logging (concise, no body dump)
- Improve schema validation error messages (concise, verbose behind DEBUG)
- Add e2e tests for error code flow and infrastructure error retry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add semantic error types to replace HTTP status code checks in runtime

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: classify run failure error codes and improve error logging

- Add RUN_ERROR_CODES (USER_ERROR, RUNTIME_ERROR) to @workflow/errors
- Populate errorCode in run_failed events via classifyRunError()
- Update web UI StatusBadge to show amber dot for infrastructure errors
- Improve world-local queue error logging (concise, no body dump)
- Improve schema validation error messages (concise, verbose behind DEBUG)
- Add e2e tests for error code flow and infrastructure error retry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: classify run failure error codes and improve error logging

- Add RUN_ERROR_CODES (USER_ERROR, RUNTIME_ERROR) to @workflow/errors
- Populate errorCode in run_failed events via classifyRunError()
- Update web UI StatusBadge to show amber dot for infrastructure errors
- Improve world-local queue error logging (concise, no body dump)
- Improve schema validation error messages (concise, verbose behind DEBUG)
- Add e2e tests for error code flow and infrastructure error retry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* address PR review comments

- Remove dead `meta` option from TooEarlyError constructor (TooTallNate)
- Extract `throwWithTrace` helper to deduplicate span recording in
  world-vercel makeRequest (TooTallNate)
- Restore `maxAttempts` const for stable retry count logging (TooTallNate)
- Fix behavioral regression: add WorkflowAPIError 404 fallback in
  suspension-handler hook disposal to handle world-vercel path where
  makeRequest doesn't map 404 to HookNotFoundError (TooTallNate)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: translate 404 to HookNotFoundError at the world-vercel boundary

Move the 404 → HookNotFoundError translation into world-vercel's
createWorkflowRunEvent, where we know the event type context. For
hook-related events (hook_created, hook_disposed, hook_received,
hook_conflict), a 404 from the server means the hook was not found.

This removes the WorkflowAPIError 404 fallback from the runtime's
suspension-handler, keeping the runtime fully decoupled from HTTP
status codes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: parse Retry-After for 425 responses and narrow hook event set

- Parse Retry-After header unconditionally so TooEarlyError gets
  the server-provided delay instead of always falling back to ~1s
- Narrow hookEventsRequiringExistence to only hook_disposed and
  hook_received (matching world-local's set), since hook_created
  and hook_conflict don't imply the hook must already exist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* rename WorkflowAPIError to WorkflowWorldError

Breaking change: rename WorkflowAPIError → WorkflowWorldError to
better reflect that this error represents world (storage backend)
failures, not HTTP API errors specifically. Updated across all
packages: errors, core, world-local, world-vercel, world-postgres,
workflow, and web.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 15:07:22 -07:00
Pranay Prakash d428d66441 [world-postgres, world-local] Fix TOCTOU races in entity state transitions (#1434)
* [world-postgres] Fix TOCTOU race in step_started that corrupts event log

The step_started UPDATE had no conditional guard on step status, allowing
a concurrent execution to revert a completed step back to 'running'. This
caused duplicate step_completed events, triggering CORRUPTED_EVENT_LOG.

Add notInArray guard to match the existing pattern on step_completed and
the DynamoDB conditional expression used in the Vercel world.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [world-postgres] Add atomic terminal-state guards to all entity UPDATEs

Add conditional WHERE clauses to match the Vercel world's DynamoDB
conditional expressions, preventing TOCTOU races where concurrent
requests could bypass pre-validation and write invalid state transitions.

Changes:
- step_started: add NOT IN (completed, failed, cancelled) guard
- step_retrying: add terminal-state guard (was unguarded)
- step_completed/step_failed: add cancelled to guard
- run_completed/run_failed/run_cancelled: add terminal-state guards
- isStepTerminal: include cancelled status

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [world-local] Add atomic terminal-state guards and concurrent race tests

Local world fixes:
- step_completed/step_failed: use writeExclusive lock to prevent
  concurrent duplicate terminal transitions
- step_started: check for terminal lock file before allowing start
- wait_completed: use writeExclusive lock (port from PR #1388)
- isStepTerminal: include cancelled status

Tests:
- Concurrent step_completed race (exactly one succeeds, one gets 409)
- Concurrent step_failed race
- step_started rejection after concurrent step_completed
- Concurrent wait_completed race

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address PR review feedback

- Fix lock file extension: use .lock instead of .json via taggedPath to
  avoid polluting entity directories with empty JSON files that cause
  SyntaxError during listing/parsing
- Make startedAt update atomic using COALESCE in SQL instead of
  deriving isFirstStart from the TOCTOU validation read
- Split changeset into separate entries per package
- Remove unnecessary context from changeset text

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address review feedback from @TooTallNate

- step_started: replace fs.access() with a fresh re-read of the step
  entity — honest about being best-effort rather than claiming atomicity
  (local world is dev-only; postgres world has SQL-level atomic guards)
- wait_completed: clean up lock file on 404 to avoid leaked lock files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: move lock files to .locks/ subdirectory off basedir

Lock files in entity directories (steps/, waits/) broke tests that
expect only tagged .json files. Move all locks to basedir/.locks/
and clean them up in clear().

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: pass ISO string to COALESCE instead of Date object

The postgres driver can't serialize a Date object inside a raw sql
template literal. Convert to ISO string for proper parameterization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: embed tag in lock file names for test isolation

Parallel vitest workers with different tags would collide on untagged
lock files, causing spurious 409s. Include the tag in the lock filename
(e.g. stepId.terminal.vitest-0) matching the pattern from PR #1388.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-03-18 12:40:06 -07:00
workflow-devkit-release-bot[bot] 5d196982a2 Version Packages (beta) (#1377)
Co-authored-by: workflow-devkit-release-bot[bot] <workflow-devkit-release-bot[bot]@users.noreply.github.com>
2026-03-18 10:21:57 -07:00
Nathan Colosimo 02ea057442 [world-postgres] Route Graphile queue execution over workflow HTTP endpoints, fix for nextjs discovery (#1417) 2026-03-17 10:00:52 -07:00
Karthik Kalyan 94c14c746b [world] When resolveData='none', only strip eventData related to refs, keep other metadata (#1364)
* add stepName with events

* add changeset

* add workflowname to run created

* add postgres migration

* update world-local

* update world-local

* preserve the fields in the original shape

* fix tests

* strip only ref/payload fields

* stub the helper into world

* add test coverage

* fix web package

* fix web package to not pass withData: true
2026-03-16 19:47:33 +00:00
Vercel Release Bot 471cb5d9c6 Version Packages (beta) (#1352)
Co-authored-by: workflow-devkit-release-bot[bot] <workflow-devkit-release-bot[bot]@users.noreply.github.com>
2026-03-12 19:40:59 -07:00
Vercel Release Bot 9c3213926f Version Packages (beta) (#1325)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-11 17:57:07 -07:00
Nathan Colosimo 3648109861 [world-postgres] [world-local] Execute Graphile jobs directly instead of defering to world-local queue (#1334)
Signed-off-by: nathancolosimo <nathancolosimo@gmail.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-03-11 16:11:07 -07:00
Vercel Release Bot 5f4395de7b Version Packages (beta) (#1306) 2026-03-10 19:25:18 -07:00
Vercel Release Bot 0063180d65 Version Packages (beta) (#1291)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-07 20:33:25 +00:00
Peter Wielander d8daa2a9a9 [world] Add method to get individual events (#1287) 2026-03-06 14:35:12 -08:00
Vercel Release Bot 58afeb25c2 Version Packages (beta) (#1284)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-06 13:46:57 -08:00
Vercel Release Bot 596d105323 Version Packages (beta) (#1279) 2026-03-06 00:20:52 +00:00
Peter Wielander 11dcb646d3 [world-local] [world-postgres] [cli] Validate local run ID and quiet dotenv logs (#1273) 2026-03-05 21:35:23 +00:00
Vercel Release Bot 597afa5174 Version Packages (beta) (#1250)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-03-05 13:01:55 -08:00
Nathan Rajlich 02f706fb99 fix(world-local, world-postgres): default hooks.list() sort order to ascending (#1275)
hooks.list() in world-local and world-postgres defaulted to descending
order, while world-vercel defaults to ascending (creation order). This
caused the webhookWorkflow e2e test to deadlock: hooks were returned
newest-first, so the test sent the first webhook request to the manual-
response hook instead of the default-response hook.

- world-local: default sortOrder to 'asc', fix getCreatedAt returning
  new Date(0) which broke ascending cursor pagination
- world-postgres: respect the sortOrder param (was hardcoded to desc)
  and default to 'asc'
2026-03-05 20:14:33 +00:00
Pranay Prakash adfe8b6b11 Add isWebhook flag to prevent hooks from being resumed via public webhook endpoint (#1270)
* Add isWebhook flag to prevent hooks from being resumed via public webhook endpoint

Hooks created with createHook() are now non-resumable via the public webhook
endpoint by default (isWebhook=false). Only hooks created with createWebhook()
set isWebhook=true, allowing them to be resumed via the public URL.

Also adds HookNotFoundError thrown by all world backends when a webhook
token doesn't match any hook, and an e2e test for the new behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix world-local: default isWebhook to false and fix test assertions

- Default isWebhook to false at write time in events-storage
- Default isWebhook to false at read time in hooks-storage (for old data)
- Update test assertions to match HookNotFoundError message

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix: default isWebhook to true for backwards compat, add postgres migration

- Revert read-side default to `isWebhook ?? true` in world-local for
  backwards compatibility with existing hooks that predate the field
- Add postgres migration 0009 to add `is_webhook` column with default true
- Update drizzle schema to match

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-05 11:04:53 -08:00
Peter Wielander ece45a0d7c Lint after biome update (whitespace changes only) (#1243) 2026-03-02 17:21:23 -08:00
Vercel Release Bot a602fcbf6b Version Packages (beta) (#1215) 2026-02-27 14:20:42 -08:00
Pranay Prakash e55c63678b Update dependencies (#1217)
Bump zod 4.1→4.3, esbuild 0.25→0.27, turbo 2.5→2.8, biome 2.2→2.4,
vitest 3.2→4.0, @vercel/functions 3.1→3.4, @vercel/oidc 3.0→3.2,
@vercel/queue 0.0→0.1, @oclif/core 4.0→4.8, AWS SDK 3.609→3.972.
Fix duplicate workspace name for nitro/nitro-v3 (turbo 2.8 strictness).
Fix vitest 4 constructor mock in world-vercel queue tests.
drizzle-orm 0.44→0.45, drizzle-kit 0.31.6→0.31.9, dotenv 16→17,
postgres 3.4.7→3.4.8, hono 4.9→4.12, @hono/node-server 1.19.5→1.19.9,
enhanced-resolve 5.18→5.19, tinyglobby 0.2.14→0.2.15,
@oclif/plugin-help 6.2.31→6.2.37, semver 7.7.3→7.7.4,
watchpack 2.4→2.5, @nuxt/kit 4.2→4.3, @nuxt/schema 4.2→4.3,
exsolve 1.0.7→1.0.8, shiki 3.13→4.0, streamdown 1.6→2.3,
lucide-react 0.469→0.575, tailwind-merge 2.5→3.5,
react-virtuoso 4.12→4.18, @xyflow/react 12.9→12.10,
@react-router/* 7.13.0→7.13.1, @testcontainers/postgresql 11.7→11.12,
lint-staged 16.1→16.2, @changesets/cli 2.29.5→2.29.8

Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-02-27 18:49:49 +00:00
Vercel Release Bot 6930e3c763 Version Packages (beta) (#1154)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-26 17:38:07 -08:00
Robert Vollmer 79a730aa68 [world-postgres] Hide also "info" logs from Graphile Worker by default (#1171)
Signed-off-by: Robert Vollmer <rovo89@users.noreply.github.com>
2026-02-23 17:17:44 -08:00
Robert Vollmer 0735b2a55b [world-postgres] Fix race conditions in Postgres streamer (#1002)
Signed-off-by: Robert Vollmer <rovo89@users.noreply.github.com>
2026-02-23 15:17:43 -08:00
Peter Wielander dda67421cf [world-postgres] [cli] Skip graphile logs for CLI json mode, observe DEBUG env (#1167) 2026-02-23 22:25:02 +00:00
Peter Wielander c9186f9870 [world-postgres] Add migrations from pg-boss to graphile-worker queue (#1126) 2026-02-20 18:25:08 -08:00
Kevin 1f9a67c759 [world-postgres] Replace pg-boss with graphile-worker (#1124) 2026-02-20 16:24:21 -08:00
Vercel Release Bot 339c44fe1a Version Packages (beta) (#1131) 2026-02-20 15:17:31 -08:00
Vercel Release Bot f4d168b012 Version Packages (beta) (#1045) 2026-02-19 15:16:28 -08:00
Nathan Rajlich 54879835f3 Fix pages router default args: use empty array instead of [42] (#1081) 2026-02-17 01:47:48 -08:00
Pranay Prakash 5e06a7c833 Materialize waits as entities to prevent duplicate wait_completed events (#1057)
* Handle 409 conflict when completing waits that were already completed

When multiple concurrent workflow invocations race to complete the same
wait, the server returns 409 (conflict) for duplicates. This change
handles the 409 gracefully in both runtime.ts (sleep elapsed check) and
runs.ts (wakeUpRun), preventing crashes and treating already-completed
waits as successful.

Also updates event-sourcing docs to reflect that waits are now
materialized as entities in storage with atomic completion guarantees.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Point e2e tests at workflow-server preview for wait materialization branch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Materialize waits as entities in local and postgres worlds to prevent duplicate wait_completed events

Adds Wait type/schema to the shared world package and implements wait entity
materialization in both local (filesystem) and postgres world implementations,
matching the DynamoDB behavior. wait_created creates a wait entity with status
'waiting', and wait_completed transitions it to 'completed' with guards that
reject duplicates (409).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix waitId to use composite key for consistency with postgres world

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Clean up wait entities on terminal run states and register waits migration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Clear WORKFLOW_SERVER_URL_OVERRIDE for merge

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changeset for all affected packages and restore server URL override

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Clear WORKFLOW_SERVER_URL_OVERRIDE now that server PR is merged

The server-side changes (vercel/workflow-server#265) have been merged to main
and deployed to production, so we no longer need to point at the preview URL.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:46:49 -08:00
Vercel Release Bot 295f6cd0b6 Version Packages (beta) (#1013)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-12 20:09:35 -08:00
Vercel Release Bot 9f442d2dd7 Version Packages (beta) (#987)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-11 15:46:37 -08:00
Vercel Release Bot d25630a27c Version Packages (beta) (#971) 2026-02-09 13:47:40 -08:00
Nathan Rajlich d9e98590fa Generate runId client-side in start() and simplify streaming types (#954)
## Summary

- Generates `runId` client-side in `start()` using ULID (`wrun_${ulid()}`), instead of deferring to the server
- Passes client-generated `runId` to `events.create()` and verifies the server accepted it
- Simplifies `runId` types from `string | Promise<string>` to `string` throughout the serialization layer
- Removes the `withResolvers<string>()` / deferred runId pattern that was needed when `runId` was server-generated
- Updates `Streamer` interface and `WorkflowServerWritableStream` to use plain `string` runId
- Updates `Storage.events.create()` first overload to accept `string | null` instead of just `null`

This is a prerequisite for E2E encryption, where we need the `runId` before serialization so it can be used as part of the encryption context.

## Test plan

All existing tests pass. Updated `start.test.ts` to expect client-generated runId, removed `writable-stream.test.ts` promise runId test (no longer applicable).
2026-02-08 00:27:40 -08:00
Vercel Release Bot 835e2fbd3c Version Packages (beta) (#921)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-06 14:20:08 -08:00
Pranay Prakash 088de0ae42 Various Performance Improvements (#932)
* Parallelize async operations in step handler for performance

- Parallelize getPort(), getSpanKind(), and world.steps.get() calls
- Start step_started event creation while hydrating arguments (CPU work)
- Parallelize step_completed event with trace serialization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix race condition: await step_started before hydration

Reverts Optimization 1 to fix a race condition where hydrateStepArguments()
could throw before stepStartedPromise was awaited, causing stale step.attempt
in the catch handler and potentially allowing extra retries.

Optimizations 0 and 2 are preserved as they don't have this issue.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Skip initial world.steps.get() in step handler for performance

Eliminate the world.steps.get() HTTP call by calling step_started first
and relying on server-side validation. This saves 50-80ms per step
execution by removing one HTTP round-trip.

The server (workflow-server) now validates:
- Step not in terminal state (returns 409)
- retryAfter timestamp reached (returns 425 with Retry-After header)
- Workflow still active (returns 410 if completed)

Changes:
- Remove world.steps.get() from initial Promise.all
- Call step_started first to get step entity and validate state
- Handle 409 (terminal state) by re-queueing workflow
- Handle 425 (retryAfter not reached) by returning timeout
- Handle 410 (workflow gone) as no-op

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add retryAfter validation to local and postgres worlds

Add server-side retryAfter validation to match workflow-server behavior:
- Check retryAfter timestamp before allowing step_started
- Return HTTP 425 with retryAfter timestamp in response meta
- Clear retryAfter field when step starts successfully

This ensures consistent behavior across all world implementations
and allows the step-handler optimization to work correctly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix step terminal state HTTP status code to 409 (Conflict)

Aligns local and postgres worlds with workflow-server, which returns
409 via InvalidOperationStateError for step in terminal state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix world-vercel telemetry to use parent application's tracer

The world-vercel package was creating spans under a separate
'workflow-world-vercel' service name, causing HTTP spans for
workflow-server API calls (step_started, step_completed) to be
filtered out when viewing traces for the main application service.

Now uses the same 'workflow' tracer name as @workflow/core to
ensure all spans are reported under the parent application's service.

* Update /demo command to include OTEL tracing with Jaeger

- Start Jaeger container for local trace visualization
- Configure OTEL exporter environment variables for dev server
- Open Jaeger UI automatically
- Add documentation about available trace attributes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add OTEL instrumentation to world-local and improve trace consistency

- Add telemetry.ts and instrumentObject.ts to world-local for tracing parity
  with world-vercel (world.runs, world.steps, world.events, world.hooks spans)
- Change workflow span name from uppercase "WORKFLOW" to lowercase "workflow"
  for consistency with step spans and OTEL naming conventions
- Add step.execute child span to trace actual user step function execution
  separately from step handler infrastructure

These changes enable local development to have the same observability as
production deployments, making performance analysis and debugging easier.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Use uppercase WORKFLOW and STEP span names for consistency with HTTP spans

HTTP spans use uppercase methods like "GET /path" and "POST /path".
Following the same convention, workflow and step spans now use:
- WORKFLOW <workflow-name>
- STEP <step-name>

Child spans (workflow.run, workflow.loadEvents, step.execute, world.events.create)
remain lowercase as they represent internal operations, not top-level entries.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add W3C trace context headers to step queue messages

Include traceparent and tracestate headers when queueing step
execution messages. This enables automatic trace propagation
by Vercel's infrastructure, potentially linking step invocation
spans to the parent workflow trace.

The trace carrier is now serialized once and included in both:
- Payload: for manual context restoration in step handler
- Headers: for automatic HTTP-based trace propagation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Improve OTEL tracing with service attribution and enhanced instrumentation

- Add peer.service attributes for workflow-server and VQS for Datadog service maps
- Rename queueMessage span to queue.publish for consistency
- Add step.hydrate, step.dehydrate, and workflow.replay spans
- Include event type in world.events.create span names (e.g., "world.events.create step_started")
- Add span.recordException() for errors with category classification (fatal/retryable/transient)
- Add span events for milestones: retry.scheduled, step.skipped, step.delayed
- Add HTTP semantic conventions with peer.service for world-vercel HTTP calls
- Add baggage propagation for workflow context (run_id, workflow_name)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update changesets to reflect all PR changes

- step-handler-parallelization.md: Add race condition fix and 409 status code fix
- world-vercel-telemetry-tracer.md: Add peer.service and event type in span names
- otel-tracing-improvements.md: New changeset for comprehensive OTEL improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Use span name for rpc.method to show event type in Datadog resource

Datadog derives the resource name from rpc.method attribute. Updated to
use the full span name (which includes event type) instead of just the
method name, so Datadog shows "world.events.create step_started" instead
of "world.events.create workflow-server".

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Use lazy ref resolution for events where client discards response data

Skip expensive S3 ref resolution (~200-460ms) for event types where
the client doesn't use the response entity data (step_created,
step_completed, step_failed, run_completed, etc). Only resolve refs
for run_created, run_started, and step_started where the client
reads the response.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changeset to include lazy ref resolution optimization

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Address PR review comments: module scope const, comment cleanup, status code audit

- Move eventsNeedingResolve Set to module scope (PR review #2)
- Rewrite changelog-style OPTIMIZATION comments as current-state docs (PR review #4)
- Fix run terminal state errors: 410 → 409 in world-local and world-postgres to
  match workflow-server's InvalidOperationStateError (409) (PR review #3)
- Fix remaining step terminal state 410 → 409 in world-postgres fallback paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 11:52:03 -08:00
Pranay Prakash 0ce46b91d9 Add x-workflow-run-id header to queue messages (#922)
* Add x-workflow-run-id header to queue messages

Pass the workflow run ID as a custom header when enqueueing messages, enabling better observability and correlation with VQS server logs.

- Add headers field to QueueOptions interface in @workflow/world
- Pass headers through to VQS client in @workflow/world-vercel
- Include x-workflow-run-id header in all queueMessage calls

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Upgrade @vercel/queue to 0.0.0-alpha.36

This version includes the headers support needed for passing x-workflow-run-id to VQS.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Pass custom headers through world-local queue implementation

Custom headers are now spread first in the fetch headers so that system
headers (x-vqs-*, content-type) take precedence and cannot be overridden.

Also adds TODO comment in world-postgres noting headers are not propagated.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 20:34:12 -08:00
Nathan Rajlich c54ba21c19 Add optional writeToStreamMulti function to the World interface (#867) 2026-02-03 17:53:36 -08:00
Vercel Release Bot 3dde14d529 Version Packages (beta) (#834) 2026-02-02 10:33:56 -08:00
Nathan Rajlich 1060f9d04a Change user input/output to be binary data at the World interface (#853) 2026-01-28 10:36:28 -08:00
Pranay Prakash 4966b728a8 implement event-sourced architecture (#621)
* perf: implement event-sourced architecture

* Apply suggestions from code review

* Improve invalid event log handling in step/hook/wait

* Handle serialized workflow run errors correctly

* log error in failing test

* Handle queue idempotency in vercel world

* hotfix for error propogation

* Fix: Incorrect HTTP status code 409 should be 410 for terminal run state rejections in postgres storage

* Fix: The code attempts to pass an unsupported `fatal` property when creating a `step_failed` event. The TypeScript schema for `step_failed` events only allows `error` and `stack` properties, so the `fatal` property causes a compilation error.

This commit fixes the issue reported at packages/core/src/runtime/step-handler.ts:133-139

## TypeScript error: Invalid property 'fatal' in step_failed event

**What fails:** TypeScript compilation fails in `packages/core` due to an invalid property in the `step_failed` event creation.

**How to reproduce:**
```bash
cd /vercel/sandbox/primary
pnpm run -F @workflow/core build
```

**Result:**
```
src/runtime/step-handler.ts(133,32): error TS2769: No overload matches this call.
  Overload 1 of 2, '(runId: null, data: { eventType: "run_created"; ... }, gave the following error.
    Argument of type 'string' is not assignable to parameter of type 'null'.
  Overload 2 of 2, '(runId: string, data: CreateEventRequest, params?: CreateEventParams | undefined): Promise<EventResult>', gave the following error.
    Object literal may only specify known properties, and 'fatal' does not exist in type '{ error: any; stack?: string | undefined; }'.
```

**Issue:** The code attempted to pass a `fatal: true` property in the `eventData` object when creating a `step_failed` event. However, the event schema defined in `packages/world/src/events.ts` for `StepFailedEventSchema` only allows `error` and `stack` properties - the `fatal` property is not part of the schema.

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>

* Fix: Code silently skips updating workflowRun if result.run is undefined, causing workflows to be incorrectly skipped instead of throwing an error

* Add hook_conflict event type for duplicate token detection

Implements hook_conflict events across all world implementations to handle
cases where a workflow attempts to use a hook token already claimed by another
workflow. Instead of throwing errors, the system now records hook_conflict
events in the event log, enabling deterministic replay.

- Add HookConflictEvent schema to @workflow/world
- Implement hook_conflict in world-local, world-postgres, and suspension-handler
- Update hook consumer to reject promises with WorkflowRuntimeError on conflict
- Add HOOK_CONFLICT error slug with documentation
- Add e2e and unit tests for conflict scenarios

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add changeset for hook_conflict events

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add unit tests for hook_conflict handling

- Add tests for hook_conflict event in workflow.test.ts
- Fix world-postgres test to not expect removed message field

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Improve hook-conflict.mdx error guide

- Remove redundant third point in 'Why This Happens' section
- Add example showing how to handle WorkflowRuntimeError for hook conflicts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix docs validation: add hook-conflict to errors index

- Fix broken link in hook-conflict.mdx (/docs/foundations/webhooks -> /docs/api-reference/workflow/create-webhook)
- Add hook-conflict to errors index page so it's discoverable by the docs link validator

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix world-local tests for hook_conflict event behavior

Update tests to expect hook_conflict events instead of thrown errors when
duplicate hook tokens are used. This aligns with the new event-sourced
approach where conflicts are recorded as events rather than thrown.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add specVersion property to World interface for backwards compatibility

- Add specVersion property to World interface to track world package version
- Add specVersion to WorkflowRun schema and run_created event data
- World implementations (vercel, local, postgres) set specVersion from npm version
- Server can use specVersion to route operations based on world version
- Add specVersion display to observability UI attribute panel
- Add spec_version column to postgres runs schema

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add migration for spec_version column in postgres schema

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add drizzle migration journal and snapshot for spec_version column

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Regenerate postgres migration using drizzle-kit

- Properly generates migration with drizzle-kit CLI
- Removes deprecated 'paused' status from enum
- Adds spec_version column

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add backwards compatibility for event-sourced runs

- Add RunNotSupportedError for runs requiring newer world versions
- Add semver-based version utilities (isLegacyVersion) to @workflow/world
- World implementations check specVersion and route to legacy handlers
- Legacy runs (< 4.1.0): run_cancelled skips event storage, wait_completed stores event only
- New runs always get current world version (4.1.0-beta.0)
- Make EventResult.event optional for legacy compatibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor spec version from semver strings to integers

Replace semver-based version compatibility with explicit integer spec versions:
- SPEC_VERSION_LEGACY (1): pre-event-sourcing runs
- SPEC_VERSION_CURRENT (2): event-sourced architecture

Use branded SpecVersion type to enforce importing from @workflow/world.
Remove semver dependency from world, world-local, and world-postgres.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Store error as CBOR in postgres world for consistency with input/output

- Add error_cbor bytea columns to workflow_runs and workflow_steps tables
- Deprecate text error column, rename to errorJson with fallback parsing
- Remove JSON.stringify from error writes (run_failed, step_failed, step_retrying)
- Add parseErrorJson helper for backwards compatibility with legacy data

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Use WorkflowRuntimeError and improve run entity handling in core runtime

- Replace generic Error with WorkflowRuntimeError for runtime assertions
- Add explicit check for run entity in run_created response
- Use run.runId instead of event.runId for consistency
- Use actual run status instead of hardcoded 'pending' in attributes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add specVersion to Step, Hook, and Event entities

- Add specVersion field to Step, Hook, and Event interfaces in @workflow/world
- Add spec_version column to steps, hooks, events tables in postgres schema
- Set specVersion to SPEC_VERSION_CURRENT when creating entities in all worlds
- Update migration to include spec_version columns for all entity tables

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor world-local storage into modular files

Split storage.ts (1041 lines) into smaller, focused modules:
- storage/filters.ts: Data filtering helpers
- storage/helpers.ts: ULID and date utilities
- storage/hooks-storage.ts: Hook CRUD operations
- storage/legacy.ts: Legacy event handling
- storage/runs-storage.ts: Run get/list operations
- storage/steps-storage.ts: Step get/list operations
- storage/events-storage.ts: Event create/list operations
- storage/index.ts: Main composition

Also extracted test helpers to test-helpers.ts for reusability.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Remove genversion and World.specVersion property

The World.specVersion string property was never actually read - only
the numeric SPEC_VERSION_CURRENT is used for backwards compatibility.

- Remove genversion dependency and generated version.ts from @workflow/world
- Remove specVersion property from World interface and all implementations
- Minor fix: correct error message to reference 'workflow' package
- Minor fix: correct error source priority in world-vercel events.ts
- Minor fix: update comment in runtime.ts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Remove genversion from world-local, world-postgres, and world-vercel

These packages no longer need genversion since we removed the
World.specVersion property in the previous commit.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Remove version.ts from .gitignore files

No longer needed after removing genversion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add legacy/backwards compatibility tests

Tests for world-local and world-postgres covering:
- Legacy runs (specVersion < 2 or null/undefined)
  - run_cancelled handling (updates run, no event stored)
  - wait_completed handling (stores event only)
  - Rejection of unsupported events
  - Hook cleanup on cancellation
- Future runs (specVersion > current)
  - Rejection with RunNotSupportedError
- Current version runs (normal processing)
- Legacy error parsing (errorJson field parsing)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add hook_received support for legacy runs

When resumeHook() is called on a legacy run (specVersion < 2), the
hook_received event was previously rejected. This adds support for
storing hook_received events on legacy runs without entity mutation,
matching the behavior of wait_completed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix missing genversion in world-vercel

Signed-off-by: Peter Wielander <mittgfu@gmail.com>

* Remove deprecated workflow_completed, workflow_failed, and workflow_started events

Replace with run_completed, run_failed, and run_started equivalents.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add specVersion to EventWithRefsSchema in world-vercel

The manually-created EventWithRefsSchema was missing the specVersion field,
which caused specVersion to be stripped when using lazy (refs) mode for events.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Wire specVersion from client through world backends

- Core runtime now sends specVersion in run_created eventData
- world-local accepts specVersion from eventData (defaults to current)
- world-postgres accepts specVersion from eventData (defaults to current)

This matches workflow-server behavior where v2 endpoints accept
specVersion from the client, while v1 endpoints default to legacy.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Move specVersion to event object level, propagate to entities

- Add specVersion to BaseEventSchema (event level, not eventData)
- Remove specVersion from RunCreatedEventSchema.eventData
- Core runtime sends specVersion on event object
- world-local reads specVersion from event, propagates to run/step/hook entities
- world-postgres reads specVersion from event, propagates to run/step/hook entities

This ensures specVersion flows from client through event to all created entities.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Update specVersion to be optional in types for backwards compatibility

- specVersion is optional in all entity schemas (runs, steps, hooks, events)
  for backwards compatibility with legacy data in storage
- Runtime always sends specVersion on event requests
- world-local and world-postgres provide fallback to SPEC_VERSION_CURRENT
- Test helpers include specVersion in all event creation calls
- EventWithRefsSchema in world-vercel defaults specVersion to 1 for legacy

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Fix world-vercel queue tests missing VERCEL_DEPLOYMENT_ID setup

Two tests were calling queue.queue() without setting up
VERCEL_DEPLOYMENT_ID, causing them to fail with "No deploymentId
provided" error before reaching the code they were testing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Add specVersion to all event creation calls in core package

The server's CreateEventSchemaV2 requires specVersion on all events,
but only run_created was sending it. This caused 400 Bad Request errors
for all other event types (run_started, run_completed, run_failed,
run_cancelled, step_created, step_started, step_completed, step_failed,
step_retrying, hook_created, hook_received, wait_created, wait_completed).

Now all event creation calls include specVersion: SPEC_VERSION_CURRENT.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-01-22 17:24:52 -08:00
Vercel Release Bot cd7c03d45d Version Packages (beta) (#819)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-22 14:12:34 -08:00
Lucas Neves 1533dbbf44 postgres-world: Delete redundant and bugged migration (#804)
* Delete redundant and bugged migration

* DCO Remediation Commit for Lucas Neves <lcneves@gmail.com>

I, Lucas Neves <lcneves@gmail.com>, hereby add my Signed-off-by to this commit: 64705dfe86

Signed-off-by: Lucas Neves <lcneves@gmail.com>

---------

Signed-off-by: Lucas Neves <lcneves@gmail.com>
2026-01-21 11:52:33 -08:00
Vercel Release Bot 101585cce0 Version Packages (beta) (#758)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-01-13 14:10:19 -08:00
Vercel Release Bot 073e26a372 Version Packages (beta) (#735) 2026-01-08 19:32:27 +01:00
Peter Wielander dd3db13d54 [world] Remove pause and resume events, actions and states (#751) 2026-01-08 19:29:17 +01:00