Commit Graph

153 Commits

Author SHA1 Message Date
Peter Wielander 8ea1532e48 [core] Combine flow+step bundle and process steps eagerly (#1338) 2026-05-04 09:53:02 +00: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
Nathan Rajlich 059821cb39 ci: pass stale-banner via path: to sticky-pull-request-comment in tests + benchmarks workflows (#1887)
* Pass stale-banner via path: to sticky-pull-request-comment instead of message:

The 'Update existing test comment with stale warning' step inlined the
previous comment body via ${{ steps.get-comment.outputs.previous-results }}
into the action's `message:` input. As the test matrix grows, the
resulting argv can exceed ARG_MAX and the action fails with
'Argument list too long' — observed on a feature branch where the
matrix doubled.

Write the rendered stale-banner message to
$RUNNER_TEMP/stale-comment.md in the github-script step and pass the
path to sticky-pull-request-comment via its `path:` input instead.
This is robust to any future matrix size.

* Apply same fix to benchmarks.yml

Same ARG_MAX hazard exists in the benchmark workflow's stale-warning
step. Apply the identical `path:`-instead-of-`message:` refactor:

- The github-script step now writes the rendered stale-banner to
  $RUNNER_TEMP/stale-comment.md and exposes the path as a step output.
- The sticky-pull-request-comment 'Update existing benchmark comment
  with stale warning' step uses `path:` instead of inlining
  ${{ steps.get-comment.outputs.previous-results }} via `message:`.

The final 'Update PR comment with results' step in this workflow
already used `path: benchmark-summary.md`; only the stale-banner
update was inlined.

* Use `github.run_started_at` for stale-comment timestamps

The 'Started at:' label was sourced from `github.event.pull_request.updated_at`,
which is the PR metadata-update timestamp — not the workflow run start
time. That made the displayed timestamp:
- coupled to PR edits (label changes, description edits, etc.) rather
  than to the actual CI run, and
- stale on workflow re-runs (an empty re-run would still show the
  original PR-update time).

Switch all six occurrences across `tests.yml` and `benchmarks.yml` to
`github.run_started_at`, the canonical "this CI run started at"
timestamp.
2026-05-04 04:09:37 +00:00
Peter Wielander 8202663857 [workbench] Add TanStack Start workbench and tests (#1875) 2026-05-04 00:42:44 +00:00
Peter Wielander 382cdf4f60 Split tarball hosting out of docs into its own project (#1893) 2026-05-04 09:10:54 +09:00
Nathan Rajlich cd50618d1f ci: switch Vercel deployment-protection bypass to OIDC Trusted Sources (#1882)
* 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.
2026-05-02 19:21:52 +09:00
Nathan Rajlich 2d66c75ad0 ci: fail fast when Next.js dev server is wedged on Windows (#1871)
A recurring Turbopack-on-Windows bug causes the dev server to enter a
'MODULE_UNPARSABLE' state during HMR in dev.test.ts, after which every
request returns 500. The remaining e2e suite then polls stuck workflows
for 60s each, burning the full 30-minute job window before getting
cancelled (~50% of recent main runs).

Bail out of the Windows e2e job as soon as dev.test.ts fails, and
health-check the dev server before kicking off test:e2e so any other
silent breakage is surfaced quickly instead of via a 30-minute timeout.
2026-04-30 00:42:09 -07:00
Nathan Rajlich 787bb15df0 ci: use GitHub API commit mode for changesets action (#1867)
The repo enforces "Commits must have verified signatures" via an
org/enterprise-level ruleset, which blocks unsigned commits pushed via
the Git CLI by GITHUB_TOKEN. Switching the changesets action to
commitMode: github-api makes commits GPG-signed by GitHub.
2026-04-29 10:34:24 -07:00
Nathan Rajlich 89738e5236 ci: stop using Release App token in release workflows (#1866)
The Release App has been temporarily removed. Switch the Release and
Backport workflows to use the default GITHUB_TOKEN, and disable the
cross-repo Front dispatch workflow until the App is restored.

Also add a workflow_dispatch trigger to release.yml so the Version
Packages PR can be created/updated manually (since pushes made by
GITHUB_TOKEN do not trigger downstream workflow runs).
2026-04-29 17:25:36 +00:00
Nathan Rajlich 3a08eaa0a1 ci: refactor wait-for-vercel-project to use GitHub Deployments API (#1861)
* ci: refactor wait-for-vercel-project to use GitHub Deployments API

Replaces the Vercel SDK / Vercel API token-based implementation with one
that resolves the deployment URL via the GitHub Deployments API:

- Find the GitHub Deployment for (target SHA, environment) where
  environment matches the Vercel-app-created "Preview \u2013 <slug>" or
  "Production \u2013 <slug>" naming pattern.
- Wait for the latest deployment status to be `success` (or `inactive`
  when Vercel skips a duplicate build, in which case its environment_url
  still points at the live deployment).
- Probe the URL to confirm the edge can route to it (any non-5xx
  response counts as live, including 401/403 from Deployment Protection
  and 404/405 from the app). Manual redirect handling treats redirects
  to vercel.com as "still building".
- Resolve the dpl_xxx deployment ID from the matching commit status
  (Vercel posts `Vercel \u2013 <slug>` statuses where target_url's last
  path segment is the inspector ID == deployment ID without the prefix).

Inputs change: project-slug + bypass-secret + github-token (with
GITHUB_TOKEN default) replace team-id + project-id + vercel-token.

Removes the @vercel/sdk dependency, shrinking the bundled dist from
5.4MB to 829KB. The VERCEL_DOCS_TOKEN secret is no longer referenced
anywhere in the repo and can be deleted from GH after this lands.

* ci(wait-for-vercel-project): drop URL probe and bypass-secret input

The GitHub Deployment status transitions to `success` only after the
Vercel app finishes building and routing is live, so an extra HTTP
liveness probe of the deployment URL was redundant. Removing it lets
us also drop the bypass-secret input \u2014 protected deployments don't
need a workaround anymore because we never make the request.

Reduces the action surface area and eliminates a runtime fetch.

* ci(wait-for-vercel-project): address PR review

- Fail loudly when the dpl_xxx deployment ID can't be resolved instead
  of returning an empty string. Consumers wire this into
  VERCEL_DEPLOYMENT_ID, which world-target uses to pick between the
  vercel and local worlds (packages/utils/src/world-target.ts), so an
  empty value would silently flip execution mode.
- Pass the GitHub App token to wait-for-vercel-project in the dispatch
  release workflow. The job sets `permissions: contents: read`, which
  blocks the default GITHUB_TOKEN from reading the Deployments API.
  The App token (already generated for workflow,front) has the
  necessary scopes.
2026-04-28 20:20:47 +00:00
Nathan Rajlich 28dc089eef ci: fix VERCEL_WORKFLOW_SERVER_* env var ternary on main (#1859)
* ci: fix VERCEL_WORKFLOW_SERVER_* ternary so main actually unsets them

In GitHub Actions expressions, '' is falsy, so the original
`cond && '' || secrets.X` pattern always fell through to the secret
regardless of branch. The result was that pushes to main were sending
preview workflow-server values to production, causing 'invalid_url'
errors on `x-vercel-workflow-api-url` across all e2e jobs.

Flip the condition so the secret sits in the truthy branch and ||
correctly selects '' on main.

* ci: skip pnpm cache in matrix-generation jobs

The Get Test Matrix and Get Community Worlds Matrix jobs only run a
small Node script to emit a JSON matrix; they never run `pnpm install`.
With `cache: 'pnpm'` set on actions/setup-node, the post-job cache save
step fails with 'Path Validation Error' because the pnpm store path was
never created, marking the whole job as failed.

Add a cache-pnpm input to setup-workflow-dev (default true) and opt out
in the two matrix-generation jobs.
2026-04-28 10:26:04 -07:00
JJ Kasper 393ffd86fc Lock next@canary test version (#1860) 2026-04-28 10:25:34 -07:00
Pranay Prakash e2ef3568a5 CI script improvements (#1826)
* CI script improvements

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

* address codex feedback: harden remaining workflows

- add allowlist regex in prepare-workbench-path to block path traversal
- move matrix/input values to env vars across e2e-vercel-prod,
  benchmarks (local/postgres/vercel), and the reusable community-world
  workflows
- validate app-name/world-id/world-package inputs in the reusable
  community-world workflows
- pipe getCommunityWorldsMatrix script output through jq -c to prevent
  \$GITHUB_OUTPUT injection

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

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 23:00:27 -07:00
Nathan Rajlich 354840e93b feat(world-vercel): support new env vars for Vercel Deployment Protection (#1824)
* feat(world-vercel): support WORKFLOW_VERCEL_PROTECTION_BYPASS env var

Allows sending a Vercel Deployment Protection bypass secret via the
`x-vercel-protection-bypass` header on all outbound requests made by
the Vercel world, enabling use against protected deployments (e.g.
previews, or workflow-server once protection is enabled).

* feat(world-vercel): support VERCEL_WORKFLOW_SERVER_URL env var

Replace hard-coded WORKFLOW_SERVER_URL_OVERRIDE constant with a function
that reads from the VERCEL_WORKFLOW_SERVER_URL env var. Allows configuring
the workflow-server URL per-deployment (e.g. workbench Preview envs
pointing to a branch deployment) without editing source.

* fix(world-vercel): preserve inline WORKFLOW_SERVER_URL_OVERRIDE const

Keep the inline const as an empty-string literal so external CI rewrite
tooling continues to work unmodified; the env var is a fallback when the
inline value is empty.

* refactor(world-vercel): rename to VERCEL_WORKFLOW_SERVER_PROTECTION_BYPASS

Align env var naming with VERCEL_WORKFLOW_SERVER_URL.

* ci: expose workflow-server protection bypass env vars to e2e-vercel-prod

Set VERCEL_WORKFLOW_SERVER_URL and VERCEL_WORKFLOW_SERVER_PROTECTION_BYPASS
on PR runs so e2e tests hit the protected workflow-server preview; leave
unset on main so production runs use the public default URL.

* refactor(world-vercel): address PR review comments

- Consolidate bypass header logic in getHeaders() to reuse
  getProtectionBypassHeader() instead of duplicating env lookup.
- Use consistent 'Authorization' casing in direct fetch() calls.
- Add unit tests for getProtectionBypassHeader, getHttpUrl, and getHeaders
  covering env var toggling and proxy/override combinations.
2026-04-21 17:43:30 -07:00
Nathan Rajlich 5889d84aef ci: auto-resolve skills/ conflicts in backport to stable (#1798)
* 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>
2026-04-17 11:10:17 -07:00
Nathan Rajlich a12c0e8511 ci: bump opencode model to claude-opus-4.7 in backport workflow (#1795) 2026-04-17 07:17:36 -07:00
Peter Wielander 71d594c5dd [ci] Skip community world E2E tests on main (#1783) 2026-04-16 18:34:18 -07:00
Nathan Rajlich 5e0a0c7ab0 Prefer stable version for docs app conflicts in backport workflow (#1791)
* 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.
2026-04-16 18:23:17 -07:00
Nathan Rajlich 69af0c1374 ci: upgrade pnpm/action-setup to v5 and read version from package.json (#1785)
* ci: upgrade pnpm/action-setup to v6 and read version from package.json

Removes hardcoded pnpm version (10.14.0) from all workflows and instead
reads the version from the packageManager field in package.json, so CI
stays in sync with the version used locally.

* ci: update setup-workflow-dev composite action to use pnpm/action-setup@v6

Also removes the pnpm-version input since the action now reads the
version from package.json#packageManager.

* ci: downgrade pnpm/action-setup to v5

v6 installs pnpm 11 RC/beta, which has a regression
(pnpm/pnpm#11264, pnpm/action-setup#225/#227/#228) that causes
'ERR_PNPM_BROKEN_LOCKFILE: expected a single document in the stream'
when the project's packageManager pins a 10.x pnpm version. v5 is the
latest stable release before v6 and supports reading the version from
package.json#packageManager.
2026-04-17 01:17:27 +00:00
Nathan Rajlich b5a723932b Improve backport workflow: auto-resolve docs and lockfile conflicts, add DCO signoff (#1770)
* 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.
2026-04-16 14:15:13 -07:00
Peter Wielander f1dfd6d29e Open Fix PR backport script env variable naming (#1755) 2026-04-15 15:01:39 -07:00
Nathan Rajlich 886dbc4e40 Use pull_request_target for backport workflow to support fork PRs (#1731)
* Use pull_request_target for backport workflow to support fork PRs

Signed-off-by: Nathan Rajlich <n@n8.io>

* Reduce GITHUB_TOKEN permissions to read-only

Signed-off-by: Nathan Rajlich <n@n8.io>

---------

Signed-off-by: Nathan Rajlich <n@n8.io>
2026-04-14 12:11:40 -07:00
Nathan Rajlich 11b85d0783 Add AI-powered conflict resolution to backport workflow (#1693)
* Add AI-powered conflict resolution to backport workflow

Signed-off-by: Nathan Rajlich <n@n8.io>

* Use anthropic/claude-opus-4.6 model identifier

Signed-off-by: Nathan Rajlich <n@n8.io>

* Address review feedback on AI conflict resolution

Signed-off-by: Nathan Rajlich <n@n8.io>

* Update .github/workflows/backport.yml

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Signed-off-by: Nathan Rajlich <n@n8.io>

* Fix script injection in gh pr create --title

Signed-off-by: Nathan Rajlich <n@n8.io>

---------

Signed-off-by: Nathan Rajlich <n@n8.io>
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
2026-04-13 22:03:24 +00:00
Nathan Rajlich 5ff51ff612 Preserve original committer identity in backport cherry-picks (#1690)
Signed-off-by: Nathan Rajlich <n@n8.io>
2026-04-10 16:12:36 -07:00
Nathan Rajlich de1c9a4fc4 Revert "Preserve original committer identity in backport cherry-picks"
This reverts commit e422bbdea1.
2026-04-10 15:17:48 -07:00
Nathan Rajlich e422bbdea1 Preserve original committer identity in backport cherry-picks
Signed-off-by: Nathan Rajlich <n@n8.io>
2026-04-10 15:11:26 -07:00
Nathan Rajlich 95f5777481 Remove redundant npm dist-tag management step from release workflow (#1651)
Changesets already handles dist-tags correctly:
- On main: pre-release mode publishes with the 'beta' tag
- On stable: GA publishes default to the 'latest' tag
2026-04-08 13:16:31 -07:00
Nathan Rajlich 1b997ae5fa Add dual-branch versioning strategy docs and backport automation (#1660)
* Add dual-branch versioning strategy docs and backport automation

Signed-off-by: Nathan Rajlich <n@n8.io>

* Add concurrency group to backport workflow

Signed-off-by: Nathan Rajlich <n@n8.io>

---------

Signed-off-by: Nathan Rajlich <n@n8.io>
2026-04-08 13:02:21 -07:00
Peter Wielander 903f26dc45 Revert "Revert "Support dual-branch releases and bump to v5 beta (#1622)" (#1624)" (#1634) 2026-04-07 11:27:44 -07:00
Peter Wielander b7c357e33e Revert "Support dual-branch releases and bump to v5 beta (#1622)" (#1624) 2026-04-06 14:56:41 -07:00
Peter Wielander 38ee590b98 Support dual-branch releases and bump to v5 beta (#1622) 2026-04-06 14:22:29 -07:00
Nathan Rajlich 9d195308c2 fix(ci): use Node 24 for releases and remove npm self-update (#1614) 2026-04-06 09:43:03 -07:00
Pranay Prakash afa3931a59 Add stress benchmarks: 1000-step, data payload, and stream tests (#1214) 2026-03-19 16:59:09 -07:00
Pranay Prakash cd4abd80fe test: improve e2e test failure diagnostics (#1426)
* test: improve e2e test failure diagnostics with run context and GitHub annotations

When e2e tests fail, automatically dump workflow run diagnostics (status,
input/output, error details, event timeline, dashboard link) to the CI
logs. Emit GitHub Actions annotations that surface on PR file diffs.
Fix collectedRunIds which was declared but never populated, enabling
observability links in the PR comment. Enrich the aggregation script
to include run IDs and dashboard URLs for failed tests.

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

* fix: increase diagnostics hook timeout and fix flaky Vercel Prod tests

- Increase onTestFailed hook timeout to 30s (default was 10s) so
  diagnostics can fetch run data even after slow test timeouts
- parallelSleepWorkflow: increase elapsed threshold from 10s to 25s to
  accommodate Vercel cold start latency
- webhookWorkflow: increase hook polling deadline from 30s to 60s and
  test timeout from 60s to 120s for slow Vercel webhook registration
- readableStreamWorkflow: stop reading once expected content is received
  instead of waiting for stream close (which can hang on Vercel), and
  increase test timeout to 120s

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

* fix: emit ::error annotations via process.stdout.write to bypass vitest ANSI prefix

Vitest's console interceptor prepends ANSI escape codes to console.log
output, which prevents GitHub Actions from parsing ::error workflow
commands. Use process.stdout.write() directly to ensure clean output.

Also enhance the custom reporter to emit annotations in onFinished
(which runs after vitest output is complete) as a reliable fallback,
and enrich failure data from the diagnostics sidecar.

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

* fix: only show observability links for vercel-prod test failures

Community world and local tests don't run on Vercel's backend, so
dashboard links are meaningless for those categories. Previously,
test name collisions across sidecar files could cause community
test failures to show Vercel dashboard URLs from vercel-prod runs.

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

* fix: link annotations to test files instead of symlinked workflow sources

The workflow source files in workbench/ are symlinks that GitHub can't
resolve, causing annotations to show raw paths like #L0 instead of
linking to code. Now:
- utils.ts: omit file= from onTestFailed annotations (just show title)
- github-reporter.ts: use the actual test file path (e.g.
  packages/core/e2e/e2e.test.ts) which GitHub can resolve

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-17 19:47:26 -07:00
Peter Wielander 40c2d95a3a Enable DurableAgent tests (#1411) 2026-03-17 00:09:02 +00:00
Nathan Rajlich 9a114b9213 ci: fix git identity for changesets Version Packages commit (#1357)
Set setupGitUser: false on changesets/action to prevent it from
overwriting our git config with the hardcoded github-actions[bot]
identity. The git identity is now configured in a prior step using
the app-slug output from actions/create-github-app-token.
2026-03-12 14:04:54 -07:00
Nathan Rajlich 7f17b075df ci: configure git identity for GitHub App bot account (#1356)
Set git user.name and user.email so that the 'Version Packages' commit
created by changesets/action is attributed to the app's bot account
instead of the default github-actions[bot].
2026-03-12 12:38:35 -07:00
Nathan Rajlich 5811beb8d9 ci: use dedicated GitHub App token instead of shared PAT (#1351)
Replace GH_TOKEN_PULL_REQUESTS (shared org PAT) with a dedicated GitHub
App token to avoid rate limiting from noisy neighbors. The app token is
generated per-run via actions/create-github-app-token, providing its own
rate limit bucket and short-lived credentials.
2026-03-12 12:00:09 -07:00
Peter Wielander 456c1aa455 Add vitest plugin for testing full workflows without setting up a server (#1237) 2026-03-05 16:08:48 -08:00
Nathan Rajlich c35b597552 fix(ci): fix npm 'latest' dist-tag not being set during release (#1255)
* fix(ci): fix npm 'latest' dist-tag not being set during release

The OIDC trusted publishing change (a78af46f) removed NPM_TOKEN from the
changesets/action step and added registry-url to actions/setup-node. This
caused the 'Add latest dist-tag' step to break for the unscoped 'workflow'
package:

- actions/setup-node writes an .npmrc at a temp path that references
  ${NODE_AUTH_TOKEN}, and sets NPM_CONFIG_USERCONFIG to point at it
- The dist-tag step was writing its own ~/.npmrc using NPM_TOKEN, but npm
  ignored it because NPM_CONFIG_USERCONFIG overrides the default path
- The OIDC token left over in NODE_AUTH_TOKEN worked for @workflow/* scoped
  packages (which already had 'latest' set) but returned E401 for the
  unscoped 'workflow' package

Fix: set NODE_AUTH_TOKEN to NPM_TOKEN_ELEVATED secret so npm picks up the
correct token from the .npmrc that actions/setup-node already configured.

Also fix silent failures: replace the piped while-read loop (which runs in
a subshell and swallows exit codes) with a for loop that tracks failures
and exits non-zero if any dist-tag operation fails.

* use process substitution instead of word-splitting for loop
2026-03-04 09:51:45 -08:00
JJ Kasper a9fea9132e Update workbench tests to build and run outside of monorepo (#1230)
* Setup fixes

* ci: run local e2e against staged tarball workbenches

* ci: update staged workbench tarball setup script

* chore: set nextjs workbenches back to next 16.1.6

* update lock

* test(e2e): resolve workbench path from WORKBENCH_APP_PATH

* fix: address deferred builder issues outside monorepo

* ci: stage tarball workbenches only for nextjs local e2e

* fix(next): discover deferred steps imported via workflows

* test(core): gate deferred step-discovery dev test to canary

* test(e2e): cover cross-file imported step in build/start lanes

* fix(e2e): use local manifest in local runs and relax dev rebuild timeout

* fix(workbench): add imported-step workflow symlink for sveltekit/astro

* test(e2e): scope imported-step workflow test to nextjs lanes

* fix(next): rebuild deferred entries on discovered file updates

* fix(next): watch transitive deferred step deps for dev rebuilds

* fix(next): restore socket-driven deferred step rebuilds

* add changeset

* chore: address review feedback on deferred e2e updates

* fix(cli): guard stream flush against closed write streams
2026-03-03 11:17:39 -08:00
Karthik Kalyan e2420bed29 fix sync front dispatch job (#1244) 2026-03-02 18:24:54 -08:00
Karthik Kalyan 596f9bf139 [workflow] dispatch front release PR sync workflow (#1224)
* sync front release PR on changeset

* Add github dispatcher for raising PRs on workflow releases on front
2026-03-02 11:57:11 -08:00
Peter Wielander d69c80b79d [ci] Disable claude code CI (#1231) 2026-03-01 12:53:39 -08:00
Pranay Prakash 45800b474b Fix OG image routing under i18n (#1176)
* Fix og route handling in i18n middleware

* Add docs CI smoke test for OG and sitemap

* Run docs OG checks against Vercel preview

* Refactor docs CI checks and rename smoke test

* Use docs Vercel token for preview checks

* Expand docs OG smoke coverage

* Check docs HTML OG metadata
2026-02-23 23:40:46 -08:00
Nathan Rajlich 81a883bc9b ci: don't cancel in-progress CI runs on main branch (#1166)
* fix: use types.isNativeError() for cross-VM Error serialization

FatalError was not properly serialized when passed from workflow code into a step function because the Error reducer checked `value instanceof global.Error` where `global` is the VM's globalThis. Errors created in the host context (like FatalError from @workflow/errors) have a different Error prototype than the VM context, so the instanceof check returned false and the error was silently dropped.

Replaced with `types.isNativeError()` from `node:util` which uses V8's internal type tag and works across VM context boundaries.

* ci: don't cancel in-progress CI runs on main branch
2026-02-24 06:50:59 +00:00
Nathan Rajlich a78af46f0e Configure Release job for OIDC trusted publishing (#1138) 2026-02-23 16:47:34 -08:00
JJ Kasper b733e2a173 Unlock canary Next.js version in tests and bump min deferred version (#1088)
* Unlock canary Next.js version

* bump min version
2026-02-16 17:27:16 -08:00
Peter Wielander 0edcccf84d [ci] Skip for CI checks when doing vercel backend testing only (#1063) 2026-02-15 18:37:17 +00:00
JJ Kasper 550997659d Update tests.yml triggers for release PR (#1048) 2026-02-14 02:31:22 +00:00