Commit Graph

849 Commits

Author SHA1 Message Date
workflow-devkit-release-bot[bot] a375a3fd85 Version Packages (beta) (#1440)
Co-authored-by: workflow-devkit-release-bot[bot] <workflow-devkit-release-bot[bot]@users.noreply.github.com>
workflow@4.2.0-beta.72
2026-03-24 17:19:33 +00:00
Pranay Prakash 672d9195a4 Fix step/workflow not found errors to fail gracefully instead of queue retry (#1452)
* feat: enhance error handling for missing workflow functions

Slack-Thread: https://vercel.slack.com/archives/C09G3EQAL84/p1773856370214769?thread_ts=1773856370.214769&cid=C09G3EQAL84
Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>

* fix: update step not found handling to match FatalError pattern

Move step function validation after step_started and call step_failed directly if not found.

Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>

* changes

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* feat: add StepNotRegisteredError and WorkflowNotRegisteredError semantic errors

Introduce dedicated error types for when step/workflow functions are not
registered in the current deployment, replacing generic WorkflowRuntimeError.
These are infrastructure errors (not user code errors) with proper error
slugs, docs pages, and a new FUNCTION_NOT_REGISTERED error code.

Step not found fails the step (like FatalError) so the workflow can handle
it gracefully. Workflow not found fails the run.

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

* fix: address PR review comments

- Remove FUNCTION_NOT_REGISTERED error code, use RUNTIME_ERROR instead
- Use .is() instead of instanceof for WorkflowRuntimeError check in runtime.ts
- Remove non-working example from WorkflowNotRegisteredError docs (custom
  errors not serialized yet)
- Update all references from FUNCTION_NOT_REGISTERED to RUNTIME_ERROR

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

* feat: add e2e tests for step/workflow not registered errors and fix docs typecheck

E2E tests:
- WorkflowNotRegisteredError: start a run with a fake workflowId, verify
  the run fails with RUNTIME_ERROR
- StepNotRegisteredError (caught): workflow catches the step failure,
  verify workflow completes and step is marked failed
- StepNotRegisteredError (uncaught): verify the run fails when workflow
  doesn't catch the error

Step not registered is tested by manually invoking useStep with a
non-existent step ID in the workflow VM — this is the same pattern the
SWC transform generates for real step calls.

Also fix docs typecheck by using declare/\@setup pattern instead of
\@skip-typecheck for code samples.

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

* fix: cast globalThis to any for Symbol index access in e2e workflow

TypeScript's strict mode doesn't allow using a symbol to index
globalThis. Cast to any since this runs in the workflow VM where
the symbol is defined.

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

* fix: classify WorkflowNotRegisteredError as RUNTIME_ERROR

The .is() check uses name-based matching, so WorkflowNotRegisteredError
(name='WorkflowNotRegisteredError') doesn't match WorkflowRuntimeError.is().
Add explicit check in classifyRunError so the error code is RUNTIME_ERROR
instead of USER_ERROR.

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

* fix: use instanceof for WorkflowRuntimeError checks, improve docs

Address PR review feedback:

1. Revert .is() checks back to instanceof WorkflowRuntimeError in
   runtime.ts and classify-error.ts. instanceof catches all subclasses
   (current and future), which is the correct behavior for these catch
   blocks.

2. Remove duplicated try/catch example from step-not-registered-error
   API reference (troubleshooting page already has it).

3. Add Callout in API reference docs clarifying that .is() works in
   server-side Node.js code but not inside "use workflow" functions
   where errors arrive deserialized from the event log.

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

* changes

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:04:42 -07:00
Nathan Rajlich 108f88d607 Fix changeset 2026-03-24 09:33:30 -03:00
Nathan Rajlich 77fd9ad355 Inline class serialization registration to fix 3rd-party package support (v2) (#1503)
* Inline class serialization registration to fix 3rd-party package support (#1480)

* Inline class serialization registration to fix 3rd-party package support

The SWC plugin previously generated:
  import { registerSerializationClass } from "workflow/internal/class-serialization";
  registerSerializationClass("class//...", ClassName);

This broke for 3rd-party packages (e.g. @vercel/sandbox) that define
serializable classes but don't depend on the 'workflow' package. The
bare 'workflow' specifier is unresolvable from within node_modules of
a package that doesn't list it as a dependency.

Now the plugin generates a self-contained IIFE that uses
Symbol.for('workflow-class-registry') on globalThis directly, with
zero module dependencies:

  (function(__wf_cls, __wf_id) {
    var __wf_sym = Symbol.for("workflow-class-registry"),
        __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = new Map());
    __wf_reg.set(__wf_id, __wf_cls);
    Object.defineProperty(__wf_cls, "classId", { ... });
  })(ClassName, "class//...");

This is fully compatible with the existing deserialization side in
@workflow/core which reads from the same globalThis registry.

* Address review feedback: fix comment and update docstring

- Fix inaccurate IIFE comment in lib.rs: the second arg is the
  generated class ID string, not the literal "classId"
- Update registerSerializationClass docstring to reflect that the
  SWC plugin now inlines equivalent logic rather than importing it

* Update CJS require fixture outputs for inline class serialization

The original PR #1480 was merged but reverted because it didn't include
updated fixtures for the CJS require patterns added by PR #1144
(custom-serialization-require-destructured and
custom-serialization-require-namespace). These fixtures still had the
old 'import { registerSerializationClass }' pattern instead of the
new inline IIFE.
2026-03-24 09:30:55 -03:00
Pranay Prakash ef4ca00b77 chore: bump next to 16.2.1 and fix deferred build (#1496)
* chore: bump next to 16.2.1

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

* test: run deferred Next dev e2e assertions on stable

Bump Next.js to 16.2.1 in docs and swc-playground and update lockfile.

* fix(next): copy all deferred step sources for step-mode transforms

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-03-23 21:25:28 -07:00
Pranay Prakash 1b4a3abbf1 chore: bump nitropack to ^2.13.1 (#1501)
* chore: bump nitropack to ^2.13.1

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

* c

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

---------

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-03-23 20:58:29 -07:00
Pranay Prakash 8366fae439 chore: bump nuxt ecosystem dependencies (#1500) 2026-03-23 20:32:58 -07:00
Pranay Prakash 362978be55 chore: bump sveltekit ecosystem (#1498)
Bump SvelteKit-related dependencies in the sveltekit workbench:
- @sveltejs/kit: ^2.43.2 -> ^2.55.0
- svelte: ^5.39.5 -> ^5.54.0
- @sveltejs/adapter-node: ^5.4.0 -> ^5.5.4
- @sveltejs/adapter-vercel: ^6.1.1 -> ^6.3.3

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 20:08:35 -07:00
Pranay Prakash 61d40ff4cf chore: bump express and fastify in workbenches (#1499)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:57:08 -07:00
Pranay Prakash 730c95bfb9 chore: bump @nestjs/* to ^11.1.17 (#1497)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:32:30 -07:00
Pranay Prakash db394c43db chore: bump hono to ^4.12.8 (#1495)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:30:01 -07:00
Pranay Prakash 7c31cd7714 Revert "Inline class serialization registration to fix 3rd-party package supp…" (#1493)
This reverts commit 7dcddb5f33.
2026-03-23 17:41:04 -07:00
Peter Wielander 01bbe66d5a [world] Add stream pagination and metadata endpoints (#1470) 2026-03-23 17:39:39 -07:00
Peter Wielander 5837d577c2 [cli] [world-local] Ensure update checks don't suggest upgrading from stable release to pre-releases (#1490) 2026-03-23 17:29:34 -07:00
Peter Wielander c1389ddf3f Remove NestJS Vercel integration while in experimental phase (#1485) 2026-03-23 17:04:31 -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
Pranay Prakash beccbc4298 feat: enforce max queue deliveries in handlers with graceful failure (#1344)
* feat: enforce max queue deliveries in handlers with graceful failure

Replace VQS maxDeliveries cap with handler-level enforcement. Handlers
now gracefully fail runs/steps after excessive queue redeliveries,
preventing "phantom stuck" runs.

- Add MAX_QUEUE_DELIVERIES constant (64) and enforce in both workflow
  and step handlers with run_failed/step_failed events
- Remove maxDeliveries from VQS trigger configs (builders + sveltekit)
- Improve world-local queue: safety limit loop, structured logging
  with runId/stepId, backoff delay on failures
- Add MAX_DELIVERIES_EXCEEDED error code

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

* address PR review comments

- Expand max-delivery comments explaining minimal-work approach
- Make workflow handler error message verbose (matching step handler)
- Fix comment: "consume the message silently"
- Reduce local queue safety limit from 1000 to 256

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

* change MAX_QUEUE_DELIVERIES to 48 and use 5s linear backoff locally

VQS uses linear 5s backoff for attempts 1-32, then exponential capped
at 2h. At 48 attempts total elapsed time is ~20h, safely under the
24h message visibility limit. Local world now uses 5s linear backoff
to approximate VQS timing.

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-23 16:58:56 -07:00
James Berry 5502438bac [world-postgres] Migrate client from postgres.js to pg (#1484) 2026-03-23 16:30:27 -07:00
Nathan Rajlich 7dcddb5f33 Inline class serialization registration to fix 3rd-party package support (#1480)
* Inline class serialization registration to fix 3rd-party package support

The SWC plugin previously generated:
  import { registerSerializationClass } from "workflow/internal/class-serialization";
  registerSerializationClass("class//...", ClassName);

This broke for 3rd-party packages (e.g. @vercel/sandbox) that define
serializable classes but don't depend on the 'workflow' package. The
bare 'workflow' specifier is unresolvable from within node_modules of
a package that doesn't list it as a dependency.

Now the plugin generates a self-contained IIFE that uses
Symbol.for('workflow-class-registry') on globalThis directly, with
zero module dependencies:

  (function(__wf_cls, __wf_id) {
    var __wf_sym = Symbol.for("workflow-class-registry"),
        __wf_reg = globalThis[__wf_sym] || (globalThis[__wf_sym] = new Map());
    __wf_reg.set(__wf_id, __wf_cls);
    Object.defineProperty(__wf_cls, "classId", { ... });
  })(ClassName, "class//...");

This is fully compatible with the existing deserialization side in
@workflow/core which reads from the same globalThis registry.

* Address review feedback: fix comment and update docstring

- Fix inaccurate IIFE comment in lib.rs: the second arg is the
  generated class ID string, not the literal "classId"
- Update registerSerializationClass docstring to reflect that the
  SWC plugin now inlines equivalent logic rather than importing it
2026-03-23 23:06:28 +00:00
openharness[bot] 90ea870add [ai] Add experimental_context to DurableAgentOptions (#1489) 2026-03-23 15:30:03 -07:00
openharness[bot] e4e0140482 [ai] Expose configured tools on DurableAgent instances (#1488) 2026-03-23 15:29:42 -07:00
JJ Kasper 6cce021503 fix(builders): catch node builtin usage when entry fields diverge (#1455)
* fix(builders): catch node builtin usage when entry fields diverge

* test(core): stabilize external package node-module build error case
2026-03-23 12:50:06 -07:00
Karthik Kalyan a89f35adbb [web-shared] Fix timeline duration format and precision (#1482)
* fix timeline precision

* fix timeline precision
2026-03-23 12:29:22 -07:00
Lucas Ralph 0d72b2d363 [cli] Add bulk cancel, --status filter, fix step JSON hydration (#1467) 2026-03-23 11:56:00 -07:00
Lucas Ralph dffe5c96ea [utils] Re-export parseName utilities and add workflow/observability module (#1453) 2026-03-23 11:52:09 -07:00
Peter Wielander da6adf7798 [o11y] Polish display when run data has expired (#1438) 2026-03-23 11:38:19 -07:00
Nathan Rajlich 992d768f80 Add CommonJS require() support for class serialization detection in SWC plugin (#1144)
* Add CommonJS require() support for class serialization detection in SWC plugin

The SWC compiler plugin now detects classes with custom serialization methods (`WORKFLOW_SERIALIZE` / `WORKFLOW_DESERIALIZE`) when symbols are obtained via CommonJS `require()` calls, in addition to the existing ESM import and `Symbol.for()` patterns.

This handles code that has been pre-compiled from ESM to CommonJS by tools like TypeScript (tsc), esbuild, or tsup, which transform:

```ts
import { WORKFLOW_SERIALIZE } from "@workflow/serde"
```

into either:

```ts
const serde_1 = require("@workflow/serde")    // namespace require
const { WORKFLOW_SERIALIZE } = require(...)    // destructured require
```

Both patterns are now recognized during the identifier collection phase, and classes using them are properly registered with registerSerializationClass().

* Address review: tighten is_require_call and extract track_serialization_bindings helper

- Tighten is_require_call to require exactly one string literal argument,
  rejecting dynamic requires and multi-arg calls.
- Extract track_serialization_bindings helper to deduplicate the require
  tracking logic between Decl::Var and ExportDecl(Decl::Var) branches.
2026-03-23 11:30:42 -07:00
JJ Kasper 5010ebe7c5 fix(next): stabilize deferred canary e2e in nextjs workbenches (#1468)
* Revert "[ci] Fix nextjs lazy disocvery impacting e2e tests, disable experimental DurableAgent tests (#1400)"

This reverts commit a2c0c7e6d9.

* fix(next): stabilize deferred discovery on canary builders

* revert canary bump

* fix(next): stabilize local e2e discovery for webpack and turbopack

* fix(next): avoid copying sdk builtins in deferred step routes

* fix(next): restore deferred sdk step registration on canary

* fix(next): stabilize deferred discovery under canary watch
2026-03-23 10:56:30 -07:00
Karthik Kalyan 2b80e2de35 [web] Support legacy newline-delimited stream format in useStreamReader (#1473)
* fix stream tab for legacy runs

* fix stream tab for legacy runs

* Apply suggestion from @VaguelySerious

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

---------

Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-03-23 09:51:28 -07:00
Peter Wielander 823f58e5c6 Revert "Add support for calling start() inside workflow functions (#1133)" (#1475)
This reverts commit e889860984.
2026-03-20 17:04:28 -07:00
Peter Wielander 78f1b0e19f [core] Support negative startIndex for streaming (#1460) 2026-03-20 13:29:42 -07:00
Peter Wielander 2b07294313 [core] Ensure open stream flush is await-able in pendingOps (#1446) 2026-03-20 13:28:05 -07:00
Pranay Prakash e889860984 Add support for calling start() inside workflow functions (#1133)
* Add support for calling `start()` directly inside workflow functions

Enable `start()` to work in workflow context by routing through an
internal step (`__workflow_start`), reusing existing step infrastructure
with no new event types or server changes needed.

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

* Address PR review feedback

- Use typeof check instead of truthiness for WORKFLOW_START symbol
- Validate start() options in workflow context (reject unsupported options like world)
- Set maxRetries=0 on __workflow_start step to prevent orphaned child runs
- Add unit tests for createStart factory (6 tests)

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

* Make Run serializable in workflow context with step-backed methods

- Add Run serialization via __serializable marker + custom Run reducer/reviver
  in the serialization module (avoids SWC plugin injecting class-serialization imports)
- Create WorkflowRun class factory (packages/core/src/workflow/run.ts) with
  step-backed methods: cancel(), status, returnValue, workflowName, createdAt,
  startedAt, completedAt, exists
- Register 8 built-in steps (__run_cancel, __run_status, etc.) in step-handler
- Update __workflow_start to return full Run object (serialized → WorkflowRun in VM)
- Update createStart to pass through step result directly
- Update docs to reflect full Run support in workflow context

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

* Fix start() in workflow VM by delegating from api-workflow stub

The workflow VM loads api-workflow.ts (via the "workflow" export condition)
which stubs all runtime functions. The start stub needs to check for the
injected WORKFLOW_START symbol and delegate to it, otherwise start() throws
"doesn't allow this runtime usage" in the workflow context.

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

* Address PR review: fix stale WORKFLOW_SERIALIZE comments and register Run in host registry

- Update comments in step-handler.ts and start.ts to reference the actual
  serialization mechanism (Run reducer with __serializable marker) instead
  of the stale WORKFLOW_SERIALIZE reference
- Register Run class in the host's class registry from step-handler.ts so
  the Run reviver can deserialize Run/WorkflowRun instances in step context

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

* Add docs for recursive/repeating workflows and deploymentId: "latest"

- Document using start() for self-chaining workflows to avoid large event logs
- Add examples for batch processing and cron-like repeating patterns
- Document deploymentId: "latest" option with type safety warning
- Update skill file with same patterns

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

* Return full Run object from startFromWorkflow e2e workflow

Update the e2e workflow to return the childRun object directly instead of
just childRun.runId, exercising Run serialization across the workflow boundary.
Update e2e test assertions to match.

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

* Add recursive fibonacci e2e test for start() in workflow

Demonstrates recursive workflow composition: fibonacciWorkflow starts
new instances of itself via start() + Promise.all to compute fib(6)=8,
fanning out across independent workflow runs.

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

* Move Run method steps to builtins with "use step" directives

Refactor: instead of manually registering Run method steps via
registerStepFunction in step-handler.ts, define them as proper "use step"
functions in builtins.ts with __builtin_ prefix. This leverages the
existing SWC plugin infrastructure — functions starting with "__builtin"
get stable bare-name step IDs.

- Add __builtin_run_{cancel,status,return_value,...} to both builtins files
- Use dynamic import() for getRun inside step bodies to avoid pulling
  Node.js modules into the workflow bundle
- Remove manual registerStepFunction calls from step-handler.ts
- Update WorkflowRun step references to __builtin_run_* names
- Fix step name display in web observability: fall back to raw name
  instead of "?" for built-in steps that don't follow step//module//fn format
- Add fibonacciWorkflow default args for nextjs-turbopack workbench UI

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

* Render Run objects as clickable links in web observability UI

- Add RunRef type and Run reviver to observabilityRevivers so serialized
  Run objects are hydrated as RunRef instead of showing raw Uint8Array
- Add RunRefInline component (purple badge with run ID) that navigates
  to the target run on click, matching the StreamRef pattern
- Thread onRunClick callback through the component chain:
  WorkflowTraceViewer → EntityDetailPanel → AttributePanel → DataInspector
- Wire up navigation in the web app's run-detail-view
- Add startFromWorkflow default args for workbench UI

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

* Throw error instead of silent fallback when Run class not in registry

Address PR review: the Run reviver now throws if the class isn't found
in the registry, instead of silently returning a plain { runId } object
that would break the assumption of getting a valid Run instance.

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

* Fix e2e failures: allow retries on Run getter steps, fix docs code samples

- Remove maxRetries=0 from read-only Run getter steps (status, returnValue,
  workflowName, etc.) — these are safe to retry and need retries when the
  child workflow hasn't completed within the step timeout. Only cancel
  keeps maxRetries=0.
- Fix docs code samples: use correct import path (workflow/api not workflow),
  add declare statements for helper functions used in examples.

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

* Use standard step//module//function naming for built-in steps

Update the SWC plugin's __builtin_ special case to generate proper
step//@workflow/core//{name} IDs instead of bare function names. This
makes parseStepName work correctly for built-in steps, showing:
- StepName: "Run#returnValue" (not "__builtin_run_return_value")
- ModuleSpecifier: "@workflow/core" (not the raw function name)

Convention: __builtin_Run_cancel → step//@workflow/core//Run#cancel
(uppercase prefix + underscore → instance method # notation)

- Move __workflow_start to builtins.ts as __builtin_start
- Rename __builtin_run_* to __builtin_Run_* for proper # notation
- Update WorkflowRun step refs to use full step// IDs
- Remove manual registerStepFunction from step-handler.ts
- Update SWC spec.md with new naming examples

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

* Remove SWC __builtin special case, use standard step naming for builtins

Remove the SWC plugin's __builtin_ special case so built-in steps get
standard step//{module}@{version}//{fn} IDs like any other step. This
makes parseStepName work correctly, showing proper StepName and
ModuleSpecifier in observability.

The VM reconstructs the same IDs via builtinStepId() which uses the
@workflow/core version to build: step//workflow/internal/builtins@{v}//{fn}

- Remove __builtin special case from SWC plugin (revert to original)
- Add builtinStepId() helper shared by workflow.ts, start.ts, run.ts
- Rename Run steps: __builtin_Run_cancel → Run_cancel, etc.
- Rename start step: __builtin_start → start
- Move start step from manual registerStepFunction to builtins.ts
- Keep __builtin_response_* names unchanged (pre-existing)

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

* Use static class methods for Run steps to get Run.method naming

Refactor Run method steps from standalone functions (Run_cancel) to
static methods on a Run class, so the SWC plugin generates step IDs
with the standard static method convention: Run.cancel, Run.returnValue,
Run.status, etc.

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

* Address PR review: tests, docs warnings, skill fix

- Add TODO on Run.returnValue about polling blocking (replace with system
  hooks once AbortSignal/AbortController PR lands)
- Add docs callout warning about returnValue holding workers alive
- Fix SKILL.md contradiction that said start() can't be used in workflows
- Enhance suspension test to assert step arguments are forwarded
- Add WorkflowRun unit tests: serializable marker, runId, registry, delegation

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

* Fix response builtins: adopt this-serialization from PR #1413

The rebase onto main didn't fully adopt PR #1413's refactor of response
builtins to use `this` instead of explicit parameters. The old pattern
(resJson(this) wrappers) passed `this` as an argument, but the step
functions now expect `this` to be set via method call context.

Switch to Object.defineProperties on Request/Response prototypes,
matching main's approach. Also document WORKFLOW_PUBLIC_MANIFEST=1
for local e2e testing.

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

* Address docs review: returnValue polling is temporary, link to start() API ref

- Update returnValue warning to note this is a temporary implementation
  that will be replaced with internal hooks
- Replace inline deploymentId: "latest" docs with link to the existing
  start() API reference which already covers it comprehensively

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

* Fix e2e tests: replace collectedRunIds with trackRun API

PR #1426 replaced the manual collectedRunIds array with a trackRun()
helper. The start() wrapper already auto-tracks, so just remove the
manual push calls and add trackRun for the child run.

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

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-20 13:00:17 -07:00
Pranay Prakash f0a5e544d0 feat: update workflow version to 1.4 and add testing documentation (#1464)
Slack-Thread: https://vercel.slack.com/archives/C0AMN8UE0TX/p1773987642556359?thread_ts=1773987642.556359&cid=C0AMN8UE0TX

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>
2026-03-20 09:22:42 -07:00
Pranay Prakash e359403555 feat: update workflow version and add namespaced streams example (#1463)
Slack-Thread: https://vercel.slack.com/archives/C0AMN8UE0TX/p1773987642556359?thread_ts=1773987642.556359&cid=C0AMN8UE0TX

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>
2026-03-20 09:20:25 -07:00
Andrew Barba 977b7e97ed changeset for projectRoot api (#1461) 2026-03-19 19:19:59 -07:00
Andrew Barba 30aba2411b fix(builders) - support explicit project roots in swc transforms (#1399)
* fix(builders) - support explicit project roots in swc transforms

* fix(builders) - normalize windows expectations in discovery test

* fix(builders) - use normalized paths in discovery plugin applySwcTransform calls

* fix(next) - reuse outputFileTracingRoot for builder projectRoot

---------

Co-authored-by: Nathan Rajlich <n@n8.io>
2026-03-20 01:07:44 +00:00
Pranay Prakash afa3931a59 Add stress benchmarks: 1000-step, data payload, and stream tests (#1214) 2026-03-19 16:59:09 -07:00
Pranay Prakash 32cc0755bd docs: document error codes and remove docs typecheck import inference (#1445)
* docs: document error codes in errors and retries guide

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

* changes

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* docs: address PR review comments on error codes guide

- Add missing `start` import from `workflow/api`
- Note that `cause.code` may be undefined for older runs
- Remove invalid `--withData` flag and use `npx workflow` CLI style
- Remove empty changeset (docs-only change needs no changeset)

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

* fix: correct WorkflowRunFailedError import and docs typecheck

- Import from `@workflow/errors` instead of `workflow` (not re-exported)
- Add WorkflowRunFailedError to import inference map
- Add myWorkflow placeholder to docs globals

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

* fix: remove import inference from docs typecheck, add explicit imports

Remove the import-inference system that auto-added imports to doc
snippets before typechecking. This was masking real issues — if a
snippet imported from the wrong module or was missing an import, the
typechecker silently fixed it instead of catching the bug.

All 24 affected doc snippets now have explicit, correct imports that
match what users would actually write.

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

* fix: remove unused sleep import from rate limiting example

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

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 13:41:23 -07:00
Pranay Prakash 73a851ada6 feat: add HookConflictError for hook token conflicts (#1448)
* feat: add HookConflictError for hook token conflicts

Replace WorkflowRuntimeError with a dedicated HookConflictError class
for hook token conflicts. This correctly classifies the error as a
USER_ERROR (duplicate token is a user mistake) rather than a
RUNTIME_ERROR.

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

* fix: use HookConflictError.is() instead of instanceof in docs

The .is() static method handles cross-VM/realm boundaries where
instanceof can be unreliable.

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-19 11:18:09 -07:00
Pranay Prakash f7bbe71e39 Add TSDoc for stepName field and document world.steps API (#1451)
* feat: add stepName field for human-readable step names

Slack-Thread: https://vercel.slack.com/archives/C09125LC4AX/p1773936163539109?thread_ts=1773936163.539109&cid=C09125LC4AX
Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>

* docs: add step examples with resolveData and hydration, document parseStepName/parseWorkflowName utilities

- Split step examples into two: one with resolveData: 'none' for efficient listing without data, and one showing how to hydrate input/output with observabilityRevivers
- Add TSDoc to stepName and workflowName fields explaining the machine-readable format and how to use parse utilities
- Document parseStepName() and parseWorkflowName() from @workflow/utils/parse-name for extracting shortName and moduleSpecifier for UI display
- Add Callout explaining the naming format

---------

Co-authored-by: v0 <v0[bot]@users.noreply.github.com>
Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>
2026-03-19 10:56:01 -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
Peter Wielander fdbe853531 Fix CLI health check for Astro/Sveltekit and add debug http logs to world-vercel (#1442) 2026-03-18 12:58:03 -07:00
Pranay Prakash 84599b7ec5 feat: classify run failure error codes and improve error logging (#1340)
- 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>
2026-03-18 12:42:50 -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
Peter Wielander 741661b0bb [world] Ensure hook_received payload is accepted as ref (#1429) 2026-03-18 12:24:47 -07:00
Karthik Kalyan 58830caa85 [web-shared][web] Fix detail panel re-rendering for encrypted runs during polling (#1437)
* prevent rerenders when input/output is decrypted

* add changeset
2026-03-18 11:33:56 -07:00
Pranay Prakash 7d535bd680 test: fix flaky promiseAnyWorkflow e2e test (#1436)
* test: fix flaky promiseAnyWorkflow e2e test

Widen the delay gap between step b (100ms) and step c (6000ms) so that
b reliably wins the Promise.any race regardless of server/network jitter.
Previously the gap was only 1s vs 3s which was insufficient in CI.

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

* Fix flaky promiseAnyWorkflow e2e test

Widened the step delay gap to stabilize the test.

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 10:47:46 -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>
workflow@4.2.0-beta.71
2026-03-18 10:21:57 -07:00
Nathan Rajlich 31bc57e513 Add encryption documentation and world features manifest (#1432)
* Add encryption documentation and world features manifest

- Add encryption.mdx under How it Works explaining end-to-end encryption
- Add features array to worlds-manifest.json (encryption for Vercel World)
- Display encryption support in WorldDetailHero and WorldCardSimple
- Add cross-references from Vercel World and Observability pages

* Derive WorldFeature type from const array

* Address review feedback: simplify key explanation, show 'No' for missing encryption, fix tooltip
2026-03-18 07:22:42 +00:00