Commit Graph

59 Commits

Author SHA1 Message Date
github-actions[bot] 2d753279d5 Version Packages (beta) (#3826)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-08-31 16:36:07 -07:00
Pranay Prakash ffc58078d0 Stop logging on healthy workflow execution (#3878)
A successful run printed several lines that described the runtime working
correctly. Most of it was fallout from defaulting the events transport to
WebSockets (#3702): three breadcrumbs written while the transport was opt-in
became default-path output, because each one reported a choice the caller no
longer makes.

- `world-vercel: using ws events transport (…)` ran once per cold start on
  every deployment, naming the transport it was always going to use.
- The `projectConfig` proxy fallback warned once per process. That World cannot
  hold a socket, so with WS on by default every CLI command and the
  observability app warned about a fallback nobody asked for and nobody can act
  on. Debug-gated and reworded from "requested but" to "unavailable for".
- The `max_duration` / `auth_expiry` drain notice is routine: the transport
  reconnects from the close that follows and no write is lost.

Swept for the same shape elsewhere:

- `world-local`'s queue-concurrency notice fired per message once a fan-out
  exceeded the limit — the semaphore doing its job.
- `@workflow/world`'s active-run recovery line printed on every dev-server
  restart with work in flight. The re-enqueue *failure* above it stays
  unconditional; that one leaves a run unresumed.
- The port-detection diagnostics in `@workflow/utils` keyed off
  `NODE_ENV=development`, which is the only environment that reaches them, so
  the gate made them unconditional for their whole audience.

All of it moves behind `DEBUG=workflow:*` via a new `debugLog` in
`@workflow/utils`, joining world-vercel's existing `httpLog` and `logRetry`
output under one selector. Warnings and errors are untouched, so a run that
actually goes wrong is no quieter than before — the ws-transport tests that
assert failures are never silent still pass unchanged.

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

Co-Authored-By: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>
2026-08-27 20:45:20 -07:00
github-actions[bot] 3c0d60be90 Version Packages (beta) (#3717) 2026-08-21 22:17:38 -07:00
Pranay Prakash f771585486 fix(world-vercel,world-local): hold process-wide state on globalThis (#3728)
* fix(world-vercel,world-local): hold process-wide state on globalThis

Both packages are bundled into the host application's server build, and a
bundler keys module identity on (resource, layer) — Next.js alone builds
`instrument`, app-route, `ssr` and `edge` layers, so one process holds one
copy of each of these modules per layer. Every module-scope `const`/`let` in
them was therefore per-copy state wearing the costume of a process singleton.

vercel/workflow#3493 made `@workflow/world-vercel` bundled rather than
external and the events WebSocket transport regressed to HTTP for exactly
this reason: the queue consumer registered its channel in the `instrument`
copy's `Map` and the write path looked it up in the route copy's empty one. A
deterministic miss, for the life of the process. `@workflow/world-local` had
the same exposure all along — including `runFileLocks`, where a duplicated
mutex simply stops mutually excluding.

Add `globalSingleton()` to `@workflow/utils` (the primitive `@workflow/core`
already hand-rolls for its World cache) and route every mutable module-scope
binding in both worlds through it.

Regression cover, in three layers:

- `global-singleton.test.ts` pins the primitive's semantics.
- `ws-transport-module-copies.test.ts` imports the module twice in one
  process and asserts a transport registered by one copy is found by the
  other — it fails on a plain module-scope `Map`, which is the shipped bug.
- `scripts/lint/module-scope-state.mjs` fails the class: an AST rule banning
  mutable module-scope state in these packages, with `// per-copy-ok: <why>`
  as the deliberate escape. Wired into both packages' `vitest run src`, with
  fixture self-tests so it cannot rot into a no-op.

* test(world-postgres): pin the module-scope-state rule for the postgres world

It is deduped today only because `getRuntimeRequire()` loads it — a property
of how it is loaded, not how it is written, and exactly what changed for
world-vercel in #3493. The package is already clean; this keeps it that way.

* docs(worlds): codify "a world must not hold mutable module state"

A world package is loaded one of two ways, and only one of them gives it a
single module instance: a runtime `require()` (deduped by Node) or the host's
bundler (one copy per layer). Which one you get is a property of how the world
is loaded, not of how it is written, and it changed under `world-vercel` in
#3493 — so the rule has to be "never rely on module scope", not "rely on it
until someone flips a config".

Written down in the four places someone can meet it:

- `docs/content/worlds/{v4,v5}/building-a-world.mdx` — a "Process-wide state"
  section for custom-world authors, with the loading modes spelled out and a
  nudge to prefer World-instance state over a global.
- `packages/world/README.md` — the same constraint on the contract package.
- `CLAUDE.md` — so the next contributor working in these packages sees it.
- `packages/core/src/runtime/world.ts` — at the two static imports, which is
  where the difference between a bundled world and a required one originates.

The rule's own error message now teaches it too, rather than naming a helper.

Consolidates the guard while here: `@workflow/utils` owns the rule and its
fixture self-tests, and sweeps every *published* `packages/world-*` discovered
at runtime, so a world package added later is covered without anyone
remembering. Each world keeps a one-assertion mirror for locality.

* style: drop prose em dashes from this branch's new text

#3704 landed a repo-wide writing pass hours after this branch was written and
took `world-vercel/src` from 406 em dashes to 130 (`ws-transport.ts` alone went
35 to 1). This branch's docs section, README, comments and lint messages were
written before that and would have put 36 of them straight back into the files
that were just cleaned.

Rewritten sentence by sentence rather than by substitution: an em dash becomes a
colon, a comma, a full stop or a parenthetical depending on what it was doing.

Also fixes a real defect the sweep surfaced: `world-postgres`'s guard test was
generated through a shell heredoc and had literal backslash-backticks in its
doc comment.

* Update .changeset/world-module-scope-state.md

Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* fix(core): build the entrypoint's queue handler from getWorld()

Adopted from #3666 by @MintedKenny, which implements #3665 and could not run
CI as a fork PR. One line of behavior: `workflowEntrypoint`'s lazy handler
init calls `getWorld()` rather than `getWorldHandlers()`.

`getWorldHandlers()` owns a second, build-time-safe cache, so calling it from
the runtime route built a *second* World in the same process. That costs a
stateful World duplicate resources on every instance — world-postgres eagerly
constructs a `pg.Pool` (default `max: 10`) and a nested world-local World in
`createWorld()`, so self-hosted users have been paying for two of each — and,
for a bundled world package, the two Worlds are built by two different module
copies, which is the mechanism behind the WS transport regression the rest of
this branch contains.

The public `getWorldHandlers()` and its separate build-time cache are
unchanged; only the runtime route stops using it.

Kept from the original: the regression test asserting the factory runs exactly
once, and the api-reference wording (re-applied over #3704's list punctuation).
Not taken: renaming the `workflow.route.get_world_handlers` span. It is a
distinct span from the per-request `workflow.route.get_world` at the top of the
flow route, and reusing that name would collide with it in traces and in
`runtime-trace-mode.test.ts`; a comment records why the name outlived the call.

Co-authored-by: Kenneth <kenneth@standardforensics.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: address AI review on the module-scope work

Two blocking findings, both real:

- **Cross-version state sharing** (`ws-transport.ts`). A process can hold two
  *published versions* of `@workflow/world-vercel` (a transitive dependency
  pinning an older `@workflow/core`, which depends on this package by exact
  version). Both wrote to the same unversioned `Symbol.for` key, so one
  version's write path could be handed a `WsEventsTransport` built by the
  other's class and frame against a protocol it may not share — with no version
  negotiation on the socket to catch it. `shapeVersion` cannot express this: the
  container is stable, the hazard is its contents. The registry and the events
  dispatcher recycler are now keyed by package version. The plain connection
  pools stay unversioned; sharing those across copies is the point.

- **The documented pattern failed the rule this PR adds.** The custom-world docs
  teach `store[StateKey] ??= …`, which the rule flagged as a field write. It now
  recognizes state rooted at `globalThis`, following one alias hop, which is
  also what `core/private.ts:23` and `next/src/index.ts:58` are already doing
  correctly (core drops 26 findings to 22, next 7 to 6). The docs also now say
  outright that `globalSingleton()` is the same thing, since AGENTS.md
  prescribes it and the page did not mention it.

Rule precision, from the review's probes:

- `.mts`/`.cts` are scanned. `@workflow/world-testing` is authored in `.mts`, so
  its entry in the sweep was passing vacuously — with the walk fixed it reports
  a real finding, now annotated (it is a standalone `serve()` entry).
- Mutations in top-level statements no longer count. A table filled at module
  evaluation is identical in every copy; divergence needs a later write.
- `static` class fields are collected, attributed to the class name.
- An *exported* binding initialized to an empty collection is a finding on its
  own, which approximates the cross-file case the walk cannot resolve.

Six fixtures pin the new behavior. The rule's header now states what it does not
see, and AGENTS.md states where the sweep stops and why core is not gated yet.

Also tags `resetGlobalSingletonForTest` `@internal`.

* fix(lint): attribute a static-field write to the field, not the class

The static-field support added in the previous commit keyed `declared` on the
class name, so a class carrying more than one mutable static reported one
finding instead of one per field, and labelled the survivor with whichever
mutation was seen first. On a two-static fixture it reported
`static Registry.latch  (`.set()`)`: the name of one field, the reason
belonging to the other, pointing the reader at the wrong line.

Key static fields `Class.field` and resolve a write to the same shape, via a
new `memberPath()` that takes the first two segments of a member chain and
tries that key before the bare root identifier. Two follow-ons fall out of
having the path:

- `this.field` inside a `static` member resolves to the class, which is the
  ordinary way to write the mutation. `staticClassOf()` returns nothing for an
  instance member, where `this` is an instance and the state is per-instance
  rather than per-copy, and nothing inside a nested `function`, which rebinds
  `this`.
- `state.count++` is now a finding, like the `state.count += 1` that
  `assignment()` already reported.

Fixtures pin all four, including the instance-field case that must stay clean.
The four world packages still report zero, and the extracted `recordMutation()`
keeps the file at its previous two Biome complexity warnings.

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

* fix: make module duplication inert across every bundled package

`@workflow/core` is bundled into the host server build the same way the worlds
are, and always has been — the original repro measured three live copies in
every arm, including the pre-#3493 external one. One instance is not reachable:
layers cannot share a module, and core cannot be external because it *is*
workflow code (`runtime/start.ts:253` and nine methods in `runtime/run.ts` are
`'use step'`), so it must go through the SWC loader. The Next integration
already encodes that rule by removing workflow-bearing packages from
`serverExternalPackages`.

So the duplication stays and the hazard is removed instead, everywhere the
duplication can happen.

`@workflow/core` (22 findings to 0): warn-once latches in `constants.ts`,
`start.ts` and `telemetry.ts`; the source-map tracer cache; the VM script cache;
the QuickJS compiled-assets and baseline caches; the dev-server port cache (its
own comment already said "per process"); the text codecs; the zstd browser
decoder; and the `useStep` closure brand, where a function marked by one copy
was invisible to another.

The one with teeth was `step-single-flight.ts`: a per-copy map is not
single-flight. Two invocations reaching it through different layers would each
believe they were alone in the process and both run the step body, silently
degrading in-process dedup to the cross-process residual its own doc scopes out
to the ownership lease.

Also `@workflow/world` (a warn-once set, hand-rolled onto `globalThis` to keep
that package dependency-free), `@workflow/ai` (the lazy OTel API), and
`@workflow/nest` (bootstrap config in a module-level `let` and two static class
fields — configure one copy, read another, and the controller is unconfigured
for the life of the process).

Five sites are deliberately per-copy and now say why: state keyed on objects
that never cross copies (the barrier safety-net `WeakSet`, the QuickJS pending
byte `WeakMap`), the synchronously-scoped guest-code sink, and the OTel
diagnostic that reports what *this* copy sees.

The sweep now covers all of it. Packages with a single module graph stay out
(build-time code, the CLI, the o11y UI, the test runner) and AGENTS.md records
which and why.

Found while doing this: two static fields on one class collapsed into a single
entry in the rule, so `WorkflowModule.options` was invisible behind
`WorkflowModule.outDir`. Statics are now keyed `Class.field`.

* fix(world): suppress noAssignInExpressions on the globalThis idiom

The hand-rolled form trips Biome, as it does in `packages/core/src/private.ts`,
which carries the same suppression. Restructuring it into a helper function
instead would hide the state behind a call the module-scope rule cannot follow,
so the binding would stop being recognized as off-module and the package would
report a finding for correct code.

* fix: sweep every bundled package, and mark utils side-effect free

@shalabhc asked on review whether `@workflow/utils` needs this too. It does,
and so do three others: `utils`, `errors`, `serde` and `workflow` all end up in
the host application's server build and none were in the sweep. All four report
zero today, which is exactly the state `world-testing` appeared to be in before
the `.mts` walk was fixed and it turned out to have a real finding. Being clean
and being *checked* are different properties, and only the second one survives
the next contributor.

`sideEffects: false` on `@workflow/utils`: verified that every module in the
package only declares (no import-time work), so a bundler can now drop the
unused parts of the barrel instead of keeping all ~64 KB of it because three
packages import one 476-byte function.

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Co-authored-by: Kenneth <kenneth@standardforensics.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Wielander <peter.wielander@vercel.com>
2026-08-21 16:55:24 -07:00
Nathan Rajlich e1e64e3de3 docs: apply Vercel technical writing standards (#3704)
* docs: apply Vercel technical writing standards

Audit the complete documentation corpus, package READMEs, skills, and
source TSDoc/comments against the vercel-technical-writing skill and
style-rules.md. Normalize sentence-case headings without changing
published anchors, remove prose em dashes and filler wording, improve
active voice and self-contained phrasing, standardize product/brand
capitalization, American English, list punctuation, units, and code
fence languages, and preserve exact runtime strings/table placeholders.

All executable code is unchanged. Modified skills have their metadata
versions bumped.

* docs: extend writing audit to repository Markdown

Apply the same technical-writing rules to design documents, compiler
specifications, workbench guides, package changelogs, and the remaining
tracked Markdown outside the deployed docs corpus. Preserve historical
meaning, commands, output literals, table placeholders, and heading
anchors.

* docs: exclude generated package changelogs from audit
2026-08-21 14:24:31 -07:00
github-actions[bot] b12f248b66 Version Packages (beta) (#3185)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-30 08:40:06 -07:00
Peter Wielander a09d00135b Revert "Statically inject workflow world target" (#2752) (#3142) 2026-07-29 08:55:29 -07:00
github-actions[bot] 741a0d9eaf Version Packages (beta) (#3087)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-28 17:20:21 -07:00
Peter Wielander 49276f2d0b [utils] Fix vercel world not being selected when running build on external CI (#3144) 2026-07-28 11:26:30 -07:00
Nathan Colosimo 62d570ed4b Remove retired v1 step route plumbing (#3061) 2026-07-24 23:50:55 +00:00
Nathan Rajlich b01ed548d7 build: declare typescript (catalog:) in every package that runs tsc (#2898)
* build: declare typescript (catalog:) in every package that runs tsc

Twenty packages invoke tsc in their build/typecheck scripts without
declaring a typescript dependency, resolving whatever tsc pnpm happens
to leave reachable. That broke locally after the TypeScript 6 upgrade
(#2700): base.json now uses the TS6-only 'types': ['*'] wildcard, and
worktrees carrying pre-upgrade node_modules/.bin/tsc shims (orphaned
typescript@5.9.3 bins that pnpm never refreshes for an undeclared
dependency) fail with TS2688 'Cannot find type definition file for *'.

Declaring 'typescript': 'catalog:' (the convention nest already
follows) makes pnpm own each package's tsc bin, so version upgrades
refresh the shims and this staleness class cannot recur. Packages
without tsc in their scripts are left unchanged.

Full pnpm build: 27/27 tasks green.

* Address review: drop duplicate zod devDep; regenerate lockfile minimally

- packages/world listed zod in both dependencies and devDependencies
  (pre-existing on main, surfaced by the devDependencies sort) — keep
  the runtime dependency only.
- Regenerate pnpm-lock.yaml from a pristine main baseline with
  --lockfile-only (a clean-main run produces zero diff, so main has no
  drift). Remaining non-typescript changes are mechanical consequences
  of the change itself: typescript is an (optional) peer of several
  tooling dependencies, so declaring it in 20 importers creates new
  peer-resolution snapshot variants and prunes the now-orphaned old
  ones; plus one radix-ui 1.6.1->1.6.2 refresh in docs caused by its
  floating 'latest' specifier.
- Validated: pnpm install --frozen-lockfile succeeds; full build 27/27.
2026-07-13 18:33:55 +00:00
github-actions[bot] ab56979d0e Version Packages (beta) (#2815) 2026-07-08 16:53:04 +00:00
Nathan Colosimo 239031ad9e fix(next): respect basePath for workflow routes (#2732)
* fix(next): respect basePath for workflow routes

* docs(core): note workflow URL resolution gap

* fix(next): expose workflow health route methods

* test(utils): remove workflow route helper tests

* test(builders): remove route handler string test

* fix(next): defer basePath validation to Next.js

* refactor(utils): remove workflow url helper wrappers

* Test Next basePath builder wiring
2026-07-06 16:43:35 -07:00
JJ Kasper 0f557d5ae4 Statically inject workflow world target (#2752)
* Statically inject workflow world target

* Fix static world injection in host bundles

* Fix static world injection gaps

* Fix Vite Nitro server startup

* Fix Nitro pg-native aliasing

* Fix static world target CI gaps

* Fix static world dev rebuild gaps

* Avoid broad runtime alias in Nitro

* Refresh Next dev route for step HMR

* Externalize Nest target world

* Use canary HMR rediscovery timeout

* Bundle local world in Nest builds

* Dedupe world target helpers and fix SvelteKit chunk patch guard
2026-07-06 14:19:45 -07:00
github-actions[bot] 166bb7bde6 Version Packages (beta) (#2692) 2026-07-06 13:32:59 -07:00
JJ Kasper f6772d95c8 Optimize Next dev HMR rebuilds (#2678)
* Optimize Next dev HMR rebuilds

* Fix Next dev HMR CI coverage

* Gate dev HMR logs behind opt-in flag

* Match workflow dev build logs to Next style

* Fix Next dev HMR changed-file classification

* Fix Windows port detection

* Relax HMR log wait in dev e2e

* Avoid canary workflow execution cache flakes

* Allow slower Turbopack HMR propagation in e2e

* Scope canary HMR fuzz execution assertions
2026-06-29 20:58:38 +00:00
github-actions[bot] df402c416b Version Packages (beta) (#2428)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-15 13:46:00 -07:00
Karthik Kalyan 926a5e7c6a otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces (#2363)
* otel: explicit traceparent injection + linked-trace mode for bounded per-invocation traces

- Add WORKFLOW_TRACE_MODE ('linked' default, 'continuous' legacy) to the
  workflow and step queue handlers. In linked mode, WORKFLOW_V2/STEP spans
  start a new trace root with span links to the incoming delivery context
  and the run-origin context, and re-enqueued messages forward the
  ORIGINAL run-origin trace carrier unchanged.
- world-vercel now explicitly injects W3C traceparent/tracestate/baggage
  headers on outgoing workflow-server HTTP requests from inside the
  client span (no-op without an OTEL SDK registered).
- New workflow.trace.mode span attribute; unit tests for both modes and
  for header injection.

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

* changeset: call out behavioral telemetry changes of the linked default

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

* docs: add v5 observability tracing page

Documents OTEL spans/attributes, linked trace mode and WORKFLOW_TRACE_MODE,
span links, context propagation, and the v4 behavior-change callout.

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

* otel: human-friendly span names for workflow and step spans

WORKFLOW_V2/STEP prefixes with full machine names (workflow//./src/...//fn)
become workflow.execute / step.execute / workflow.start with the short
function name. New workflowDisplayName/stepDisplayName helpers in
@workflow/utils handle both raw and queue-sanitized name forms; full names
remain in the workflow.name/step.name attributes.

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

* changeset: merge span-name and linked-trace notes into one changeset

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

* docs: update trace-shape prose to renamed span names

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

* docs: replace ascii trace diagram with mermaid

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

* address review: empty carriers, shared trace helpers, mode warning, name edge cases, consumer span kind

- Treat an empty ({}) trace carrier as absent everywhere the trace-mode
  logic branches, so linked mode falls back to a fresh origin instead of
  forwarding a useless {} forever; workflow.trace.propagated now reports
  whether a usable carrier arrived.
- Extract the duplicated linked-mode logic into shared telemetry helpers
  getNextTraceCarrier() and buildInvocationSpanLinks(), used by both the
  workflow and step queue handlers; resume-hook now uses
  linkToTraceCarrier (gaining the isSpanContextValid guard).
- Warn once per distinct unrecognized WORKFLOW_TRACE_MODE value instead
  of silently selecting linked.
- shortNameFromSanitized: map default/__default to the module short name
  (mirroring parseName) and document the `$`-sanitization limitation.
- Queue-delivered workflow.execute spans now use the CONSUMER span kind,
  matching queue-delivered step.execute spans; docs span table and
  changeset updated accordingly.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:35:53 -07:00
Pranay Prakash 3867270be8 Reduce unnecessary CI runtime (#2151)
* Reduce unnecessary CI runtime

* Fix shared E2E artifact extraction path

* Stabilize getWorkflowPort timeout test on Windows

* Preserve UI unit coverage on CI fast path
2026-06-02 02:49:20 +00:00
github-actions[bot] 2f19552035 Version Packages (beta) (#2140)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-28 11:19:23 -07:00
github-actions[bot] b885f1f2d1 Version Packages (beta) (#1888)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-05-11 13:21:13 -07:00
Pranay Prakash 1203dae70c Friendlier workflow errors (consolidated) (#1849)
* Introduce structured context-violation errors + Ansi renderer

Phase 1: Add Ansi rendering helpers (frame, hint, note, help, code, inline)
to @workflow/errors, and a chalk mock for readable snapshot tests.

Phase 2: Add four context-violation error classes to @workflow/core
(NotInWorkflowContextError, NotInStepContextError,
NotInWorkflowOrStepContextError, UnavailableInWorkflowContextError)
and apply them to all twelve user-facing throw sites so errors now
include docs links and a structured "what/why/fix" frame.

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

* Address review: tighten changeset, implement ansifyName, harden Ansi

- Tighten phase 1 changeset to a single sentence (per pranaygp review) and switch to double-quoted frontmatter (per Copilot + repo convention).
- Implement `ansifyName` to actually apply dim styling to workflow/ / step/ prefixes; add an `Ansi.dim` helper to `@workflow/errors` so callers don't need to import chalk directly.
- Remove the `void getWorkflowMetadata;` workaround in context-errors.ts by dropping the unused value import (we only needed the type and symbol).
- Render the plain-Error throw in `workflow/get-workflow-metadata.ts` with `Ansi.frame` + docs link so the VM path matches the structured-class styling from the sibling step path (still uses a plain Error to avoid the module-init cycle).
- Guard `buildUnderline` against zero-length markers so a stray empty token can't produce a negative `String.repeat` count.

* Structured runtime logger metadata + fold in replay-timeout logging

Adds a `.child()` and `.forRun(runId, workflowName)` child-logger API to
the structured logger so runtime/step code doesn't have to repeat
`workflowRunId`/`workflowName`/`stepId` on every call. Normalizes error
metadata to structured `errorName` / `errorMessage` / `errorStack` fields
instead of ad-hoc `error: err.message` strings, and adds comments to
silent catches that swallow expected idempotency conflicts.

Also folds in the pending changes from #1812 so that PR can be closed:

- Standardize the console prefix to `[workflow-sdk]`.
- Split the replay-timeout log into a warn-while-retrying vs.
  error-when-giving-up, and surface the underlying error when we can't
  mark a timed-out run as failed.
- Include the error stack in the "Fatal runtime error during workflow
  setup" log and in the top-level user-code workflow error log so the
  stack surfaces in flattened log drains.
- Drop the `[Workflows] "<runId>" - ` prefix from
  `buildWorkflowSuspensionMessage` — the structured logger now attaches
  run context.

Supersedes #1812.

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

* Use double-quoted changeset frontmatter per repo convention

* Add SerializationError + apply to user-facing serialization sites

Phase 4 of friendlier errors: introduce a `SerializationError` class with
an optional `hint` and a docs link (workflow-sdk.dev/err/serialization-failed),
and adopt it at every user-facing serialization boundary in @workflow/core:

- Locked ReadableStream at a workflow boundary
- Unregistered class / missing `classId` / missing `WORKFLOW_DESERIALIZE`
- Attempting to return step functions to clients or call workflow functions
  directly
- Webhook `respondWith()` called outside a step
- `dehydrate*` / `getSerializeStream` failures (workflow args/return, step
  args/return, stream chunks)

Internal invariants (format prefix length checks, unknown format bytes,
missing `STREAM_NAME_SYMBOL`, encryption key/size guards, etc.) now throw
`WorkflowRuntimeError` instead of plain `Error` so the classifier and logger
treat them consistently.

`formatSerializationError` now returns `{ message, hint }` so the hint
fragment can be rendered with the standard SerializationError framing
instead of being baked into the message string.

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

* Use double-quoted changeset frontmatter per repo convention

* Presentation-only user vs SDK error attribution

Add describeError() that derives attribution and class-aware hints from
existing error classes + RUN_ERROR_CODES — no event data changes. Wire into
step failures, max-delivery exhaustion, run failures, and fatal setup errors
so terminal logs include errorAttribution and a hint for known error types.

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

* Address review: describeError accepts precomputed errorCode + instanceof

- `describeError(err, errorCode?)` now accepts an optional precomputed
  `RunErrorCode`. `classifyRunError(err)` only narrows to USER_ERROR /
  RUNTIME_ERROR, so the REPLAY_TIMEOUT and MAX_DELIVERIES_EXCEEDED branches
  were previously unreachable from the step / run failure log sites.
  Callers that know the failure category (runtime.ts for replay timeout and
  max-deliveries exhaustion) now pass the code in.
- Context-violation checks use `instanceof` against the actual classes from
  context-errors.ts instead of a name-string set. Type-safe + survives
  class renames.
- Wire the new hints through to the REPLAY_TIMEOUT and MAX_DELIVERIES_EXCEEDED
  log sites so those branches actually render a hint now.
- 3 new tests cover the reachable code paths + precomputed-code override.
- Changeset frontmatter switched to double quotes per repo convention.

* Cosmetic consistency pass on remaining bare throws

Internal invariants now use WorkflowRuntimeError so describeError attributes
them to the SDK: missing startedAt, VM generateKey, closure-vars outside
step context, ENOTSUP. defineHook().resume() formats schema validation
failures as a readable list instead of a JSON blob.

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

* Use double-quoted changeset frontmatter per repo convention

* Data-driven describeRunError + expose via @workflow/core/describe-error

Observability renderers read persisted run_failed / step_failed event data,
not live Error instances. describeRunError takes { errorCode, errorName }
and returns the same { attribution, hint } shape as describeError, so the
CLI and web UI can derive user-vs-SDK framing from the event log directly.

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

* Friendlier build-time errors: WorkflowBuildError class + applications

Add `WorkflowBuildError` class in `@workflow/errors` with optional `hint`
for an actionable next step, and apply it in `@workflow/builders` at
user-facing sites: failed esbuild phases, unresolved built-in steps, and
empty esbuild output now throw `WorkflowBuildError` with a hint pointing
at the likely fix. Runtime invariants remain plain `Error`.

* Polish friendlier-errors rendering: drop functionName leak, simplify docs link, redirect stack

- Drop the readonly `functionName` param-property on context-error classes so
  util.inspect no longer prints a trailing `{ functionName: 'foo()' }` block.
- Replace the `DocLink` ("label: https://…") shape with a plain `DocsUrl`
  template-literal type. Error output now renders a single clean line:
  `docs: https://…` (new `Ansi.docs` helper) instead of the noisier
  "note: Read more about foo(): https://…".
- Add throw helpers (`throwNotInWorkflowContext`, etc.) that call
  `Error.captureStackTrace(err, stackStartFn)` on V8 engines so the top frame
  of the thrown error points at the user's call site instead of at the gate
  function inside the framework. Callers pass themselves as the boundary.
- Refactor `defineHook()` (both root and `/workflow`) to use named function
  closures rather than `this.create`/`this.resume`, since the stack redirect
  relies on a stable function identity that survives destructuring.
- Update context-errors.test.ts to snapshot the new `docs:` framing and to
  add a regression test asserting the top stack frame is the user call site.

* Consolidate friendlier-errors stack: fix ANSI leak + non-retry semantics

Addresses PR review feedback across the 8-phase friendlier-errors stack and
fixes issues surfaced by manual testing (createHook() inside a step):

- ANSI no longer leaks into .message / .stack. Context-violation errors
  now store plain text on .message and render the colored framed form
  lazily via [util.inspect.custom] / toString(). Structured logs, log
  drains, CBOR-serialized events, and JSON payloads no longer contain
  raw \x1B[...m bytes.

- Context violations are now fatal. ContextViolationError sets
  fatal = true; FatalError.is(err) recognizes any error with a
  fatal: true own property. Calling createHook() from a step no longer
  burns three retry attempts on a guaranteed-to-fail context violation.

- Ansi helpers moved to @workflow/errors/ansi subpath so imports from
  @workflow/errors no longer pull chalk into consumers that only want
  error classes (addresses reviewer VaguelySerious).

- Shared redirectStackToCaller helper in packages/core/src/capture-stack.ts,
  used by both context-errors.ts and workflow/get-workflow-metadata.ts
  (addresses Copilot review on #1849).

- Structured framed content: ContextViolationError now takes a structured
  FramedContent (title segments + detail branches) and renders plain/pretty
  from the same source of truth.

Tightens the eight existing phase changesets to 1-2 sentences each and adds
four new scoped changesets (errors-ansi-subpath, context-errors-plain-message,
context-errors-fatal, capture-stack-shared) for the followup fixes, so the
final changelog history stays readable.

* test: update step-handler mocks for scoped forRun() logger

The runtime logger now uses .forRun(runId, name, {stepId, stepName})
to attach scope context, so 409-handling log calls no longer repeat
{workflowRunId, stepId} in every metadata bag — those live on the
scoped logger instance. Update the mock to return itself from forRun()
and tighten assertions to check both the log args (errorName/errorMessage)
and the forRun() scope.

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

* Mark SerializationError fatal + route dehydration through step-failure path

SerializationError now carries readonly fatal = true. Step-return
dehydration is wrapped inside the user-code try/catch so that the
resulting error flows through userCodeFailed → step_failed →
FatalError.is() short-circuit instead of bubbling up as HTTP 500 and
triggering a queue retry loop. Retrying a step that returned a non-POJO
is guaranteed to fail the same way, so this saves ~20s and 3 near-
identical error blocks per serialization failure.

* Add logging snapshot tests + manual-test artifacts

Snapshot tests lock in the exact shape of:
- describeError() payloads (attribution, errorCode, hint) for every
  classification — plain Error, SerializationError, context-violation,
  WorkflowRuntimeError, REPLAY_TIMEOUT, MAX_DELIVERIES_EXCEEDED.
- The scoped-logger call signature for the two canonical runtime
  failure paths (fatal-bubble and hit-max-retries), so refactors of
  forRun() / child() metadata merging can't silently change what users
  see in their log drains.

SerializationError now also has a direct test for readonly fatal=true
+ FatalError.is() recognition.

pr-artifacts/ contains real log-output snapshots from running the
nextjs-turbopack workbench against five error scenarios. These are
reference material for reviewers and are flagged to be removed before
merge.

* Readable step-fatal logs: inline stack + friendly step/workflow names

The step-level fatal-error log used to embed the full stack trace inside
an `errorStack` string field in the metadata object, so util.inspect
rendered it as a quote-escaped, line-continuation blob when the log
hit the terminal — unreadable in practice. Move framing + stack into
the log *message* (matching the workflow-level log in runtime.ts) and
keep the metadata object compact with only the indexable structured
fields (`errorAttribution`, `errorName`, `errorMessage`, `hint`,
IDs). Log drains still get the same keys; humans now see a readable
stack trace.

Also introduce `formatStepName` / `formatWorkflowName` in
`@workflow/utils` that render machine names
(`step//./workflows/1_simple//add`) as `add (./workflows/1_simple)` in
log framings, using the existing `parseStepName` / `parseWorkflowName`
parsers. Applied to step-fatal, hit-max-retries, exceeded-max-retries,
and workflow-threw log sites.

Artifacts in pr-artifacts/ updated to show the new output shape, and
renamed .log → .md since they're Markdown and IDE previews are nicer
that way.

* Opinionated pretty formatter for runtime structured-log metadata

Replace util.inspect's default object dump (which quote-escapes
multi-line stacks and paragraph hints into a single-line JSON-y blob)
with a workflow-aware formatter that composes the entire log line
into a single string passed to console.error / console.warn.

Highlights of the new output:
- Per-run / per-step IDs render with their parsed friendly names so
  users see `wrun_… · simple (./workflows/1_simple)` instead of just
  the raw `workflowName: 'workflow//./workflows/1_simple//simple'`.
- Color-coded attribution badge (user error red / sdk error magenta)
  paired with the error class in bold.
- Hints render as a paragraph under `hint:` rather than a backslash-
  `\n`-escaped string.
- Drops redundant fields (errorStack always; errorMessage when it's
  already in the parent message) to avoid double-printing.
- Unknown fields fall through as a sorted `key  value` tail so we
  never silently drop log information.

@workflow/errors/ansi gains bold/red/magenta helpers used by the
formatter. The web / web-shared packages don't consume stderr — they
read structured event payloads from the World event log — so this is
presentation-only at the runtime layer.

* ci(benchmarks): disable pnpm cache for getCommunityWorldsMatrix

The job never runs `pnpm install` (it just calls `node` against a
checked-in script), so the pnpm store path never exists. The post-job
`actions/setup-node@v4` cache-save then fails with `Path Validation
Error: Path(s) specified in the action for caching do(es) not exist`
and red-X's the entire job even though the matrix step succeeded.

The setup-workflow-dev composite already has a `cache-pnpm` opt-out
input for this exact case — wire it through here.

* Address PR review comments: inspect dedup, cause leak, retry-loop tests

- ContextViolationError: util.inspect(err) duplicated every framed detail
  line because the stack-tail strip only sliced the first message line.
  V8's Error.stack reads `Name: messageLine1\n  messageLine2\n  at ...`,
  so for our multi-line `title\n╰▶ docs: …` messages every detail line
  was getting prepended twice (once in the pretty form, once via the
  unsliced message tail). Count the actual message lines and slice past
  all of them. Repro test asserts `╰▶ docs:` appears exactly once.

- WorkflowError: stop assigning `cause: undefined` as an enumerable own
  property when no cause is provided. Subclasses (every error in this PR)
  inherit the parent constructor; the unconditional assignment polluted
  `util.inspect(err)` output with `{ cause: undefined, … }` on every
  no-cause instance. The `super(...)` call already conditionally sets
  `.cause` non-enumerably when `options.cause` is provided.

- step-handler.test.ts: add a regression-gate suite that exercises the
  fatal-vs-retryable retry-loop wiring directly. Asserts that an error
  with `fatal: true` produces exactly one `step_failed` event with no
  `step_retrying`, and that a non-fatal `Error` retries via
  `step_retrying` on early attempts and emits `step_failed` once the
  retry budget is exhausted. Catches the silent-regression case where
  `fatal = true` is removed from a context-violation error class but
  the `FatalError.is()` unit tests stay green.

* Consolidate changesets + remove pr-artifacts

Address review feedback to drastically shorten the changesets — fold
the 15 file-by-file entries into a single user-facing changeset for
@workflow/core / errors / builders / utils. Also drop the pr-artifacts/
folder (reviewer-only log captures, no longer needed).

* Polish runtime error logging: layout, stack trim, hint consolidation

Five user-driven fixes from manual smoke-testing of #1849:

1. Logger layout. composeLogLine() now puts the structured-fields block
   (attribution badge, run/step IDs, error code) **between** the framing
   line and the stack body, instead of after it where 30+ lines of stack
   buried the most useful information. The framing stays at the top,
   stack at the bottom, structured info readable at a glance.

2. Stack trim. Drops framework-internal frames (`node_modules/.pnpm/`,
   `node:internal/`, Turbopack-bundled `node_modules__pnpm_*` chunks,
   `_next_dist_*` chunks) and caps the surviving frame count at 6
   so the stack stays compact even on heavy async wrappers. Suppressed
   runs emit one summary line so users know the trim happened.

3. Wrapper-route noise. The nextjs-turbopack workbench's start route
   was catching `WorkflowRunFailedError` rejection on
   `Promise.race([readLoop(), run.returnValue])` and re-logging it via
   `console.error('Error in workflow stream:', error)` plus
   `controller.error(error)` — which then triggered Next.js's
   `⨯ failed to pipe response` overlay. The SDK already logs the
   failure cleanly upstream and the runId is on the response header, so
   the wrapper now closes the SSE stream cleanly on
   WorkflowRunFailedError.

4. Consistent framed `╰▶ hint:` / `╰▶ docs:` layout for all errors
   that carry a hint or docs slug. WorkflowError, SerializationError,
   and WorkflowBuildError now share one `appendFramedDetails` helper
   matching the box-drawing structure that ContextViolationError
   already used. Was: blank-line-separated `Learn more: <url>`. Now:
   one tree, indistinguishable from context-violation rendering.

5. Drop the duplicate logger-side `hint` field. Hints now live on the
   error message only — actionable hints get serialized into the event
   log, rehydrated on the workflow side, and shown in observability
   automatically. The previous logger-only hint duplicated stderr but
   never made it past the step boundary.

   Updated SerializationError hint to point at the foundations doc
   ("Ensure you're returning workflow serializable types. Check the
   serialization docs to see what's serializable:
   https://workflow-sdk.dev/docs/foundations/serialization") instead
   of the hardcoded `(plain objects, arrays, primitives, …)` list,
   which drifted out of sync as the supported types grew. Same hint
   reuses for step args, workflow args/return, stream messages, and
   any other site that goes through `formatSerializationError`.

Also retitled the retry summary `3 retries` → `3 max retries` since
"3 retries" next to "4 attempts" was ambiguous (already-happened vs.
budget).

* Trim error-card title + drop machine step name from persisted error

- ErrorStackBlock (web observability): show just the first non-empty
  trimmed line of the error message in the card title with single-line
  truncation. Multi-line messages (`Failed to serialize step return
  value\n╰▶ hint: …`) were rendering the entire framed body in the
  title, pushing the copy button off-screen and burying the
  scannability of the headline. Full message stays in the body via
  the stack (V8 prepends `Name: message` to `Error.stack`), so no
  information is lost; hover-tooltip exposes the full title text.

- Persisted error message: drop the `Step "step//./.../foo"` machine
  name from `Step failed after N retries: …` and `Step exceeded max
  retries (…)` strings. Observability already attributes the event
  to a specific step via the UI tree, and the CLI logger emits the
  friendly `Step foo (./...) hit max retries` framing on its own
  line. Embedding the raw `step//./...` machine name in the persisted
  message text was duplicate noise.

* Update .changeset/friendlier-errors.md

Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* Update .changeset/pretty-log-format.md

Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>

* Update SerializationError snapshot tests for slug-less message

The class no longer attaches a slug-based `╰▶ docs:` line — the
foundations URL is embedded directly in the hint via the
`formatSerializationError` helper in @workflow/core. Update the test
expectations accordingly:

- bare-title case is now a single line (no docs link)
- hint case renders one `╰▶ hint: …` branch (no second branch)

* Update serialization.test.ts hint assertions for foundations URL

Four `should throw error for an unsupported type` cases were still
asserting on the old hardcoded type list. Update to the new hint
phrasing that points at the foundations doc, matching the change in
`formatSerializationError` (`packages/core/src/serialization/errors.ts`).

---------

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
2026-05-04 05:04:24 +00:00
workflow-devkit-release-bot[bot] 5714c2335a Version Packages (beta) (#1711) 2026-04-16 15:34:32 -07:00
Nathan Rajlich 173756dc4d [docs] Rename workflowdevkit to workflowsdk and useworkflow.dev to workflow-sdk.dev (#1759)
* [docs] Rename workflowdevkit references to workflowsdk

* [docs] Rename useworkflow.dev to workflow-sdk.dev

* [chore] Add changeset for domain rename

* [docs] Revert sitemap rewrite to useworkflow.dev (crawled-sitemap not yet available for new domain)
2026-04-15 18:01:48 -07:00
workflow-devkit-release-bot[bot] a261b2128b Version Packages (beta) (#1635) 2026-04-07 17:37:59 -07:00
Nathan Rajlich 44a18048a5 Reset package versions to 4.0.0 so changesets produces 5.0.0-beta.0 (#1648)
The previous pre-release versions (4.x.y-beta.N) caused two issues:
- semver.inc('4.0.0-beta.N', 'major') returns 4.0.0, not 5.0.0
- Pre-release numbers carried over (beta.61 -> beta.62 instead of beta.0)

Setting all versions to 4.0.0 (non-pre-release) ensures a clean major
bump to 5.0.0-beta.0. Also removes @workflow/swc-playground-wasm from
the changeset and pre.json since it is a private package.
2026-04-07 14:45:40 -07:00
Harpreet cdf90d5a38 Rename Workflow DevKit to Workflow SDK, remove beta badge, add tweet wall (#1541)
* Rename Workflow DevKit to Workflow SDK, remove beta badge, add tweet wall

- Rename "Workflow DevKit" to "Workflow SDK" across all files (~108 files)
- Rename standalone "WDK" references to "Workflow SDK"
- Remove beta badge from homepage hero
- Add tweet wall component to homepage with 4 builder testimonials

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

* Rename Workflow DevKit to Workflow SDK, remove beta badge, add tweet wall

- Rename "Workflow DevKit" to "Workflow SDK" across all files (~108 files)
- Rename standalone "WDK" references to "Workflow SDK"
- Remove beta badge from homepage hero
- Add tweet wall component to homepage with 4 builder testimonials

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Harpreet Arora <harpreet.txt@gmail.com>

* Address review: fix missed trigger phrase renames and bump skill versions

- Rename "workflow devkit" to "workflow sdk" in trigger phrases for both skill files
- Bump workflow-init SKILL.md version to 1.1
- Bump workflow SKILL.md version to 1.5
- Note: CLAUDE.md is a symlink to AGENTS.md, already renamed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Harpreet Arora <harpreet.txt@gmail.com>

* link correct tweet

---------

Signed-off-by: Harpreet Arora <harpreet.txt@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Karthik Kalyanaraman <karthik.kalyanaraman@vercel.com>
2026-03-29 16:05:39 -07:00
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
Lucas Ralph dffe5c96ea [utils] Re-export parseName utilities and add workflow/observability module (#1453) 2026-03-23 11:52:09 -07:00
Vercel Release Bot a602fcbf6b Version Packages (beta) (#1215) 2026-02-27 14:20:42 -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
JJ Kasper 3d770d5385 Expose workflows manifest under diagnostics folder (#998)
* Expose workflows manifest under Next.js diagnostics folder

* feat(builders): emit diagnostics manifest for vercel world

* dedupe function

* update changeset

* fix(builders): write vercel diagnostics manifest under workflows

* fix(builders): rename vercel diagnostics manifest path
2026-02-10 17:25:19 -08:00
Nathan Rajlich 73bf7be925 Change compiler ID generation logic to use Node.js import specifier (#899)
## Summary

This PR changes how the SWC compiler generates IDs for workflows, steps, and classes. Instead of using raw file paths, IDs are now based on **Node.js module specifiers** when the file belongs to a package (either in `node_modules` or a workspace package).

## Motivation

Previously, IDs were generated using file paths like `step//src/jobs/order.ts//fetchData`. This caused several issues:

1. **Package exports conditions**: When a package uses conditional exports (e.g., `"workflow"` vs `"default"` conditions in `package.json`), the same import specifier can resolve to different files. Using file paths meant IDs could differ based on which export condition was used.
2. **Cross-bundle consistency**: Classes serialized in one bundle couldn't be deserialized in another if the file paths differed.
3. **Version tracking**: No way to include package versions in IDs for cache invalidation.

## Changes

### New ID Format

IDs now use the format `{type}//{modulePath}//{identifier}` where `modulePath` is either:

- A **module specifier** like `point@0.0.1` or `@myorg/shared@1.2.3` for package files
- A **relative path** prefixed with `./` like `./src/jobs/order` for local app files

Examples:

- `step//workflow@4.0.1-beta.50//fetch` (SDK step)
- `step//./workflows/order//processOrder` (local step)
- `class//point@0.0.1//Point` (package class)
- `class//./src/models/User//User` (local class)

### New Module Specifier Resolution

Added `packages/builders/src/module-specifier.ts` which:

- Detects if a file is in `node_modules` or a workspace package
- Finds the nearest `package.json` and extracts name/version
- Returns the module specifier for the SWC plugin to use

### SWC Plugin Changes

- Added `moduleSpecifier` option to plugin config
- Updated `naming.rs` to support both module specifiers and relative paths
- Added `get_module_path()` helper that uses specifier when available, falls back to `./filename` format

### Special Cases

- **Builtin functions** (`__builtin_*`): Continue to use just the function name as the ID for stable, version-independent lookup from the workflow VM runtime.

## Testing

- Updated all 125+ SWC plugin test fixtures to use new ID format
- Added tests for module specifier resolution
- Added tests for Windows path normalization in naming

## Breaking Changes

This is technically a breaking change for any persisted workflow runs that reference the old ID format. However, since IDs are internal implementation details and not user-facing, this should not affect end users.

## Files Changed

- `packages/builders/src/module-specifier.ts` - **NEW**: Module specifier resolution logic
- `packages/builders/src/apply-swc-transform.ts` - Pass module specifier to SWC plugin
- `packages/builders/src/base-builder.ts` - Use `getImportPath` for virtual entry imports
- `packages/swc-plugin-workflow/transform/src/lib.rs` - Accept and use module specifier
- `packages/swc-plugin-workflow/transform/src/naming.rs` - New ID formatting with module paths
- `packages/swc-plugin-workflow/spec.md` - Updated documentation
- `packages/core/e2e/e2e.test.ts` - Updated test assertions for new ID format
2026-02-04 14:23:02 -08:00
Vercel Release Bot 3dde14d529 Version Packages (beta) (#834) 2026-02-02 10:33:56 -08:00
Nathan Rajlich b16a6828af Move "parse-name" into the utils package (#814) 2026-01-23 10:57:52 -08:00
Nathan Rajlich dc44dab49d Biome config tweaks and fixes applied (#741) 2026-01-07 12:52:52 -08:00
Vercel Release Bot 6d7e5035a4 Version Packages (beta) (#711) 2025-12-31 10:17:49 -08:00
Nathan Rajlich 9b1640d76e Do not include initial attempt in step function maxRetries count (#703)
Closes #592.
2025-12-31 00:59:50 -08:00
Peter Wielander 80955e7212 [cli] [web] Allow opening web UI without config validation, show better UI when runs can't be found (#684) 2025-12-28 17:55:24 +01:00
Vercel Release Bot e6b1bef5bb Version Packages (beta) (#693) 2025-12-27 12:40:09 -08:00
Vercel Release Bot 96a4d3a2fd Version Packages (beta) (#676)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-24 09:51:14 -08:00
Peter Wielander 0cf0ac3211 [util] Extract helper function to find local world dataDir across CLI/UI (#682) 2025-12-23 20:42:42 +01:00
Vercel Release Bot fc6a32d6dc Version Packages (beta) (#607) 2025-12-16 21:05:18 +01:00
Adrian 1ef6b2fdc8 fix: use workflow health endpoint to check for port (#616)
* chore: use workflow health endpoint to check for port

* chore: update comment

* changeset

* Fix: The JSDoc comment for the `probePort` function is outdated and doesn't match the implementation. It claims the function returns true for "non-404 response" but the code now checks for exactly 200 status.

This commit fixes the issue reported at packages/utils/src/get-port.ts:251

## Outdated JSDoc comment in probePort() function doesn't match implementation

**What fails:** The JSDoc comment at line 251 of `packages/utils/src/get-port.ts` claims the `probePort()` function `@returns true if the port responds as a workflow server (non-404 response)`, but the actual implementation at line 269 explicitly checks `return response.status === 200;`

**How to reproduce:**
1. Read the JSDoc comment for `probePort()` function in `packages/utils/src/get-port.ts` (line 251)
2. Read the implementation at lines 268-269
3. Observe the mismatch: the comment says "non-404 response" but code checks for exactly status 200

**What happened vs expected behavior:**
- The code behavior was changed in commit `34cb235` (Dec 15, 2025) from checking `response.status !== 404` to checking `response.status === 200`
- The internal code comments were updated in commit `5840ab2`, but the JSDoc was left outdated
- Developers reading the JSDoc would incorrectly believe the function accepts any non-404 response (400, 405, etc.) when it actually requires exactly 200

**Fix:** Updated the JSDoc `@returns` line to accurately reflect the implementation: `@returns true if the port responds with a 200 status from the health check endpoint`

**Verification:** All 19 tests in `packages/utils/src/get-port.test.ts` pass with the updated documentation.

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

---------

Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
2025-12-15 16:34:44 -08:00
Vercel Release Bot 7018f490e6 Version Packages (beta) (#589)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-12 18:11:25 -08:00
Adrian c9b8d843fd fix: improve port detection reliability with HTTP probing (#590)
* add logs

* fix: sveltekit type error

* feat: add polling for ports

* cleanup logs

* changeset

* fix test

* test

* test

* trigger

* trigger

* trigger

* trigger
2025-12-10 13:06:19 -08:00
Vercel Release Bot 9916f2a4a6 Version Packages (beta) (#447)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-12-02 10:12:48 -08:00
Adrian cd451e0295 chore(utils): replace execa dependency (#476)
* chore(utils): replace execa dependency

* changeset
2025-12-01 14:15:25 -08:00
Adrian bc9b6282c2 fix: @vercel/nft parsing proc file paths in sveltekit adapter (#473)
* fix: build proc paths dynamically

* test

* changeset
2025-12-01 13:00:42 -08:00