* ci: stop deploying changeset-release/main, run its e2e against production
The changesets action force-pushes `changeset-release/main`, and it can
point at exactly main's HEAD SHA. Vercel keeps one commit status per
project per SHA, so when both a production deployment (from main) and a
preview deployment (from changeset-release/main) are built for the same
commit, whichever finishes last owns the status. On 2026-07-30 the
preview finished last, so `vercel/wait-for-deployment-action` — which
reads the deployment ID out of that status — handed production e2e runs
a preview deployment ID and forked runs across environments.
Disable git deployments for that branch in every Vercel project rooted
in this repo, and give the changeset PR's Vercel e2e lanes a deployment
to test that actually exists: main's production deployment for the PR's
base SHA, resolved by SHA so a mid-flight production build is waited out
rather than silently replaced by an older one.
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
* ci: resolve changeset-release e2e deployments with the wait action, tokenless
Per review: with changeset-release/main no longer deployed, main SHAs
can never again be deployed to a second environment of these projects,
so the per-SHA commit status the action reads is unambiguous for
exactly this lane. Reuse vercel/wait-for-deployment-action with
environment: production and sha pinned to the PR base SHA instead of
the Vercel-API polling script, drop the script and its VERCEL_TOKEN
usage, and inherit the action's inactive/skipped-build handling.
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
---------
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
* otel(world-vercel): inject trace context on v4 event requests
The v4 event path (createEvent / getEvent / listEvents) routes through
fetchV4 → global fetch with a custom undici dispatcher, bypassing both the
makeRequest path (where the explicit W3C trace-context injection lives) and
ambient undici auto-instrumentation. As a result, v4 event traffic from the
flow route carried no traceparent, so workflow-server could not parent its
spans to the invocation — its spans never joined the /flow execution trace,
even though v2/v3 reads/writes (via makeRequest) did join.
fetchV4 now calls injectTraceContextIntoHeaders before fetch, the single
choke point for all v4 create/get/list requests, mirroring makeRequest.
No-op when no OpenTelemetry SDK is registered.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(agents): require trace-context injection on new world-vercel HTTP paths
Codify the guardrail that the v4 regression revealed: any outgoing
world-vercel request must call injectTraceContextIntoHeaders (auto-
instrumentation can't be relied on with the custom dispatcher / global fetch),
with a test in trace-propagation.test.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* changeset: make v4 trace-propagation note concise
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat: add hook ready promise
* test: cover hook ready continuation scheduling
* feat: replace hook.ready with hook.hasConflict (Promise<boolean>)
- hook.hasConflict resolves true when the token is owned by another
active hook, false once registration is committed — no throw, so
workflows can branch on conflicts early. Awaiting it suspends the
workflow to commit the hook registration (createHook alone does not).
- Chain the already-created fast-path through promiseQueue so
resolution order matches event-log order (review feedback).
- Skip inline step execution when a suspension has an awaited hook
creation so the hasConflict continuation can advance independently
of step execution (review feedback).
- Update unit tests, e2e tests, workbench workflows, and v4/v5 docs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: fix inconsistent hasConflict bullet in create-webhook reference
State both resolution values explicitly (true = token already owned,
false = registered) instead of a parenthetical that only described the
false case.
* docs: require docs preview links in PR descriptions for docs changes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: restore SWC Plugin heading in AGENTS.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
Drops the label-based backport override in favor of workflow_dispatch.
The pull_request_target trigger has security concerns (it runs with
write permissions on PR-controlled events), and we already have a
manual dispatch path that covers the same use case.
* Hoist AI model env, fix opencode external_directory permission, fail loud on AI infra errors
Three related fixes triggered by the failed run on #1935:
1. Hoist the AI model name to a top-level `AI_MODEL` env var
(`anthropic/claude-opus-4.7`); both `opencode run` invocations
now interpolate `vercel/${AI_MODEL}` so the model is specified in
exactly one place.
2. Switch `OPENCODE_PERMISSION` from the bare-string shortcut
`"allow"` to the explicit object form
`{"*":"allow","external_directory":"allow"}`. The shortcut
was observed not to override `external_directory` (which defaults to
"ask" and auto-rejects in non-interactive `opencode run`),
causing the conflict-resolution AI to fail when reading scratch files
it created under `/tmp/`.
3. The `Resolve conflicts with opencode` step no longer uses
`continue-on-error`, and now distinguishes two outcomes via an AI-
written outcome file (`.backport-conflict-outcome.json`):
- `{"status":"resolved"}` — the legitimate clean path; cherry-pick
continues and the backport PR is opened.
- `{"status":"unresolved", ...}` — the legitimate "AI couldn't
do it, hand off to a human" path; `resolved=false` is set and the
conflict-failure comment is posted on the source PR.
- Anything else (missing file, malformed JSON, unknown status) is
treated as an opencode/AI Gateway infra failure: the step exits
non-zero, the workflow fails red, and the misleading
"couldn't resolve" comment is suppressed.
The prompt + scratch files are also moved into the workspace so
opencode never needs `external_directory` access anyway.
* Allow manual workflow_dispatch with ref+model inputs; use AI_MODEL in PR body
Add a `workflow_dispatch` trigger to the backport workflow with two
optional inputs:
- `ref` — commit SHA on `main` to back-port (defaults to `main` HEAD)
- `model` — overrides the default AI model used by opencode for the
decision and conflict-resolution steps (defaults to the workflow's
hardcoded `AI_MODEL`)
The top-level `AI_MODEL` env var now uses
`${{ inputs.model || 'anthropic/claude-opus-4.7' }}` so manual runs
pick up the override without changing anything else.
Manual dispatch (like the `backport-stable` label) always forces a
backport regardless of any AI verdict — the operator's intent is
explicit by virtue of triggering the workflow. The PR body shows
"Triggered manually via `workflow_dispatch`." in that case.
The PR body's conflict-resolution attribution also now interpolates
`${AI_MODEL}` (e.g. "opencode with `anthropic/claude-opus-4.7`")
instead of hardcoding "Claude Opus" so the text stays accurate if the
default model is later changed.
* Address PR review: also detect leftover conflict markers in staged files
The previous `Resolve conflicts with opencode` sanity check used
`git diff --diff-filter=U` to detect unresolved cherry-pick conflicts,
which only catches unmerged index entries. That misses the case where
the AI runs `git add` on a file that still has `<<<<<<<` /
`=======` / `>>>>>>>` markers in its content — git happily stages
the broken file as a normal modification.
Add a second check using `git diff --check --cached`, which emits
`leftover conflict marker` lines when any staged content still has
the standard markers. Grep specifically for that phrase so unrelated
whitespace warnings don't trip the check. Also update the inline
comment to accurately describe what each check covers (per Copilot's
review on #1943).
* Restructure backport workflow with AI-driven decisions
Run the backport workflow on every push to main and have AI analyze each
commit to decide whether to recommend a backport to stable, instead of
relying on a manual backport-stable label. The action now always opens a
PR for human review and never pushes directly to stable. The
backport-stable label is preserved as a manual override that forces a
backport regardless of the AI verdict.
* Address PR review: randomized output delimiters and updated manual instructions
- Use uuidgen-based delimiters when writing multiline values (PR title,
body, AI reasoning) to $GITHUB_OUTPUT, so user-/model-controlled
content cannot collide with or inject into the heredoc terminator.
- Update the manual conflict-resolution instructions to push a backport
branch and open a PR against stable, matching the new "never push
directly to stable" policy.
- Document the head-commit-only behavior of the push trigger inline in
the workflow.
* ci: switch Vercel deployment-protection bypass to OIDC Trusted Sources
The e2e, benchmark, and docs-smoke CI jobs previously used the static
`VERCEL_AUTOMATION_BYPASS_SECRET` deployment-protection bypass token
to reach protected Vercel deployments. Switch them over to the new OIDC
Trusted Sources flow: the GitHub Actions runner mints a short-lived
OIDC token via `core.getIDToken()` and forwards it on requests in the
`x-vercel-trusted-oidc-idp-token` header.
Each workbench project (and `workflow-docs`) has been configured with a
matching trusted-source rule:
aud=https://github.com/vercel, repository=vercel/workflow
The shared header helper now lives at `scripts/trusted-sources-headers.mjs`
and is imported by both the e2e/bench tests and the docs smoke script,
removing the previous duplication.
* rename to VERCEL_OIDC_TOKEN and wire through world-vercel
- Rename the env var from VERCEL_TRUSTED_OIDC_TOKEN to VERCEL_OIDC_TOKEN
to match Vercel's convention (also read by @vercel/oidc's
getVercelOidcToken()).
- In @workflow/world-vercel, replace the legacy
VERCEL_WORKFLOW_SERVER_PROTECTION_BYPASS / x-vercel-protection-bypass
flow with VERCEL_OIDC_TOKEN / x-vercel-trusted-oidc-idp-token. The
trusted-source header is attached on every outbound workflow-server
request (both proxied through api.vercel.com and direct).
- Drop the bypass header from the encryption-key and
resolve-latest-deployment fetches: those go to api.vercel.com which
is public.
- Drop VERCEL_WORKFLOW_SERVER_PROTECTION_BYPASS plumbing from tests.yml.
- Update the pending world-vercel changeset to describe the final
trusted-sources flow.
* .
* .
* ci: add statuses:read permission for wait-for-vercel-project action
The action queries /commits/{sha}/status (Commit Statuses API) in addition
to the Deployments API, in order to extract the Vercel `dpl_...` ID. With
an explicit permissions block in place, GITHUB_TOKEN now needs
`statuses: read` or the action 403s when resolving the deployment ID.
Reported by Copilot review on #1882.
* ci(docs): log status code and body when waitForServer times out
Helps diagnose deployment-protection / OIDC-trusted-source bypass
failures (e.g. SSO redirects) on the workflow-docs preview.
* ci(docs): log OIDC token claims (aud, repository, etc.) for diagnostics
Helps determine whether the bypass is failing because of missing
trusted-source config, claim mismatch, or audience mismatch.
* ci(docs): add curl debug step to verify OIDC header reaches Vercel
* .
* ci: remove debug logging now that trusted-sources config is correct
The fetch-failure root cause was the trusted-sources rule format: the
labs workbench projects had been PATCHed with just `to.slugs` (no
`preset`), but Vercel's edge requires the dashboard-form-style
`to.preset: 'all-custom'` field plus `development` in the slug list to
match incoming requests. After re-PATCHing all projects with the
correct format, the bypass works end-to-end.
* ci(docs): debug — test trusted-sources bypass against docs and labs deployments
Trying repository_owner claim added to one labs project to see if that
fixes the bypass.
* ci(docs): revert curl debug step
The GitHub Actions OIDC trusted-sources bypass returns 401 on all tested
projects regardless of claim configuration (including workflow-docs which
was set up via the dashboard). This is not a per-project config issue.
Need to investigate with Vercel team before continuing.
* ci(docs): probe trusted-sources bypass and surface x-vercel-id
Adds a debug step that does two HEAD requests against the docs preview
deployment (with and without the OIDC trusted-sources header) and prints
the response status line plus `x-vercel-id` for each. The proxy-side
trusted-sources changes for GitHub Actions OIDC tokens are rolling out
gradually (~12+ hours), so the edge-node identifier in `x-vercel-id`
helps explain why a request might succeed or fail during the rollout
window.
Also includes `x-vercel-id` in the `waitForServer` timeout error so
post-mortem analysis of failing runs has the same edge-node info.
* ci(docs): drop trusted-sources curl probe — bypass works once proxy fix reaches the serving edge node
The probe served its purpose: confirmed the bypass is functional once
the request lands on a region that has the proxy-side trusted-sources
fix rolled out. The waitForServer error message still surfaces
x-vercel-id for any future rollout-window debugging.
* .
* world-vercel: log outbound OIDC token claims once per process
Adds a one-shot diagnostic that prints the non-sensitive claims of the
OIDC token (`iss`, `aud`, `owner_id`, `project_id`, `environment`,
`sub`, `scope`, `exp`) on the first request that uses bearer auth.
This is invaluable for debugging Vercel deployment-protection
trusted-source rule mismatches: a 401 from the edge tells you nothing
about why the rule didn't match, and the token's claims are the only
thing that determines that. The signature is never logged.
Gated to once per process — Vercel-issued tokens are process-stable for
the lambda's lifetime so further log lines would just be redundant
spam.
* world-vercel: route trusted-sources header through getVercelOidcToken()
The Authorization bearer correctly preferred config.token (a static
Vercel auth token from CLI / Actions runner) and fell back to
getVercelOidcToken() inside a Vercel function. But the trusted-sources
bypass header (x-vercel-trusted-oidc-idp-token) was being read directly
from process.env.VERCEL_OIDC_TOKEN inside getHeaders(). That env var is
the bake-time token, frozen at deployment-creation time — on a project
that has been redeployed after a settings change, it carries stale
claims (e.g. an iss from when the project was briefly in 'global' mode)
that no longer match the workflow-server's trusted-sources rule.
Move trusted-sources header attachment from getHeaders() (sync) to
getHttpConfig() (async) and source it from getVercelOidcToken(). That
function reads getContext().headers['x-vercel-oidc-token'] first — a
freshly minted per-request token that always reflects current project
settings — and only falls back to the env var when that header is
missing.
Bearer auth source remains config.token-first.
Also expand the diagnostic to log claims from BOTH the per-request OIDC
token AND the bake-time env var so the divergence is visible in logs
when debugging future trusted-source mismatches.
Removes the now-misleading getProtectionBypassHeader() helper (its
'read env var directly' semantics were exactly the bug).
* world-vercel: skip OIDC trusted-sources header on proxied path
The two outbound flows have different auth requirements:
1. Proxied (usingProxy=true) — calls api.vercel.com/v1/workflow.
Public endpoint, authenticated with a static Vercel auth token via
config.token. The api-workflow proxy mints its own OIDC token
before forwarding to workflow-server, so the trusted-sources
bypass header on the SDK→proxy hop is meaningless. CLI, GitHub
Actions, and other API-client callers take this path.
2. Direct (usingProxy=false) — runs inside a Vercel deployment
talking straight to workflow-server. workflow-server validates a
Vercel OIDC bearer; Vercel's edge validates the trusted-sources
header. Both must come from getVercelOidcToken() (the per-request
fresh token), not process.env.VERCEL_OIDC_TOKEN (the bake-time
token that can be stale after a project config change).
Previously getHttpConfig attached x-vercel-trusted-oidc-idp-token on
both paths whenever getVercelOidcToken() resolved. That accidentally
forwarded the GitHub Actions OIDC token (when wired into
VERCEL_OIDC_TOKEN by the test runner) onto every SDK→proxy request,
which is harmless but wrong-by-design — the proxy is public, doesn't
look at that header on its inbound side, and the GHA token isn't its
intended audience.
Bearer auth source rules:
- Proxied: only config.token. (No fallback to OIDC; that auth
pathway doesn't go through the proxy's auth checks.)
- Direct: config.token (for tests / local dev), falling back to
getVercelOidcToken() (for Vercel-runtime calls).
* world-vercel: throw if proxied path is hit without a Vercel auth token
The api-workflow proxy authenticates the caller with a regular Vercel
auth token (not OIDC), so reaching the proxied path with no
config.token is always wrong: the proxy will reject the request and
the SDK caller would see an opaque 401 with no actionable hint.
Throw at config-resolution time with a clear message that points to
the WORKFLOW_VERCEL_AUTH_TOKEN env var the SDK reads from. Adds tests
covering both the no-token-throws case and the with-token-attaches-
bearer-and-skips-trusted-sources case.
* test(e2e): include x-vercel-id in startWorkflowViaHttp error message
When the trusted-sources bypass returns 401, the error message now
surfaces the response's x-vercel-id header so we can identify which
edge node served the failure. Helps distinguish proxy-rollout
incompleteness from actual config errors during incremental
rollouts of edge-side changes.
* ci: mint GHA OIDC tokens on demand to survive 5-minute expiry
GitHub Actions OIDC tokens have a hard 5-minute lifetime that cannot be
extended (no API to ask for a longer TTL — exp is always iat + ~300s).
Pre-minting once at the start of the job and shipping the result down
to the test runner via env var means tests that run late in the suite
hit an expired token and 401 on /api/trigger-pages (and any other
trusted-sources protected endpoint).
Move minting into scripts/trusted-sources-headers.mjs:
- getTrustedSourcesHeaders() is now async.
- It calls the runner's ACTIONS_ID_TOKEN_REQUEST_URL endpoint directly
(the env vars GHA exposes when permissions: id-token: write is on)
and re-mints 60s before the cached token's exp.
- Falls back to process.env.VERCEL_OIDC_TOKEN for non-GHA contexts
(Vercel runtime, local dev).
Workflow files drop the now-redundant 'Mint OIDC token' step and the
VERCEL_OIDC_TOKEN env-var passthrough on the test step. The runner env
vars propagate to subsequent steps automatically.
Updates all 17 callers in e2e.test.ts / bench.bench.ts / utils.ts /
docs/scripts/check-docs-smoke.mjs to await the now-async call.
* address PR #1882 code review
- Drop `statuses: read` from the three workflow permission blocks (the
wait-for-vercel-project action works without it on a public repo).
- Revert the `x-vercel-id` debug logging in `startWorkflowViaHttp`.
- Delete `packages/world-vercel/src/jwt-claims.ts` (debug-only helper).
- Drop the JWT claims diagnostic logging from `getHttpConfig`.
- Tighten the auth-flow comment in `getHttpConfig` and remove the
historical 'no longer attaches' note from `getHeaders`/its test.
- Restore `.changeset/world-vercel-protection-bypass.md` (already
shipped in a beta release per .changeset/pre.json).
- Trim the `.changeset/world-vercel-trusted-sources.md` description to
one short paragraph.
* docs(AGENTS): document local VERCEL_OIDC_TOKEN via vercel env pull
Configured trustedSources.projects on all 11 workbench app projects so
each one accepts a Vercel-issued OIDC token from any of the others. A
developer running e2e locally can now do `vercel env pull` from any
workbench app's directory and use the resulting VERCEL_OIDC_TOKEN to
bypass Deployment Protection on any of the workbench preview/prod
deployments — no need to disable protection on the project just to run
the suite locally.
* docs: tighten changeset description guidance in AGENTS.md
Specify that changeset descriptions should be one sentence (two at
most), and remove the redundant BREAKING CHANGE marker guidance since
breaking changes are already communicated via the major semver bump.
* docs: address Copilot review feedback
- Align command reference to `pnpm changeset add` (matching line 188)
- Drop the "see existing changesets for examples" parenthetical since
existing changesets predate the new sentence-limit guidance
* ci: auto-resolve skills/ conflicts in backport to stable
The skills/ directory is not maintained on the stable branch (skill
files are unrelated to npm packaging). Extend the backport workflow's
auto-resolution — and the AI-assisted fallback prompt — to treat
skills/ conflicts the same way docs app conflicts are handled: keep
the stable side and drop the incoming change from main.
Also updates AGENTS.md to document which directories are stable-only
placeholders and how the backport action handles them.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Nathan Rajlich <n@n8.io>
---------
Signed-off-by: Nathan Rajlich <n@n8.io>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* Prefer stable version for docs app conflicts in backport workflow
After #1786 restored a minimal Next.js placeholder docs app on stable,
docs app conflicts should resolve to the stable branch version rather
than being deleted. Only docs/content/ is actively maintained on stable,
so conflicts there should still be resolved normally.
Update both the auto-resolution logic in backport.yml and the AI prompt
to reflect the new policy, and update AGENTS.md to match.
* Use git show :2:$file to detect ours-side presence in conflicts
git ls-files --error-unmatch succeeds for unmerged paths even when
the file only exists on the incoming (theirs) side, which would then
fail on git checkout --ours. Use git show :2:$file to specifically
check for a stage-2 entry, which indicates the file exists on the
ours (stable) side.
* Auto-resolve docs/ and pnpm-lock.yaml conflicts in backport workflow
The docs/ directory is not maintained on the stable branch. When cherry-picking
from main to stable, any conflicts in docs/ files are now auto-resolved by
deleting them. Lockfile conflicts are resolved by re-running pnpm install.
If these resolve all conflicts, the cherry-pick pushes directly to stable
without needing AI resolution or a separate PR.
* Add --signoff to cherry-pick to pass DCO check
* Preserve docs/content/ in backport conflict resolution
The docs/content/ directory is kept on stable because the markdown
files are bundled into npm packages via prepack scripts. Update the
conflict auto-resolution to only delete docs app files (outside of
docs/content/), and update AGENTS.md accordingly.
* Address review: setup pnpm before cherry-pick, fix grep pipefail, guard lockfile resolution
- Move pnpm/node setup before the cherry-pick step so pnpm install
is available during conflict resolution
- Add || true to the docs grep pipeline to prevent pipefail exit
when there are no non-content docs conflicts
- Only run pnpm install for lockfile conflicts when no other
conflicts remain, to avoid choking on conflict markers
* Let pnpm resolve lockfile conflicts natively
* Remove redundant Setup Node.js step for opencode path
Node.js is now set up unconditionally at the start of the job for
the cherry-pick step's pnpm install, so the conditional setup for
the opencode path is redundant.
* 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>
* Bring back sitemap
* Bring back sitemap
* Add pre commit hooks for sitemap
* Add pre commit hooks for sitemap
* Fix failing queue test
* Revert queue test failure
* Add unit tests