Skip to content

fix(core): re-route runs delivered to the wrong deployment - #2960

Merged
alangenfeld merged 2 commits into
mainfrom
alangenfeld/fail-cross-deployment-runs
Aug 5, 2026
Merged

fix(core): re-route runs delivered to the wrong deployment#2960
alangenfeld merged 2 commits into
mainfrom
alangenfeld/fail-cross-deployment-runs

Conversation

@alangenfeld

@alangenfeld alangenfeld commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary & Motivation

A queue callback that reaches a deployment other than the one its run is pinned to derives the per-run encryption key from the wrong master key, so the delivery fails before user code runs and the run dies as a blank "exceeded max retries". The delivery is re-enqueued explicitly addressed to the run's own deployment — strictly better-targeted than the send that misrouted — and the run is failed with the new DEPLOYMENT_MISMATCH error code only once WORKFLOW_DEPLOYMENT_MISMATCH_MAX_RETRIES (default 3) is spent. Gated on the new World capability deploymentAffinity, so worlds with synthetic or version-tagged deployment ids are unaffected.

Test Plan

Unit tests added for the guard and both runtime paths; local vitest and typechecks pass.

@changeset-bot

changeset-bot Bot commented Jul 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6921d21

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 21 packages
Name Type
@workflow/core Patch
@workflow/errors Minor
@workflow/world Minor
@workflow/world-vercel Patch
@workflow/builders Patch
@workflow/cli Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/vitest Patch
@workflow/web-shared Patch
@workflow/web Patch
workflow Patch
@workflow/world-testing Patch
@workflow/world-local Patch
@workflow/world-postgres Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch
@workflow/nuxt Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview Aug 5, 2026 6:10pm
example-nextjs-workflow-webpack Ready Ready Preview Aug 5, 2026 6:10pm
example-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-astro-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-express-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-fastify-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-hono-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-nestjs-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-nitro-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-nuxt-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-sveltekit-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-tanstack-start-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workbench-vite-workflow Ready Ready Preview Aug 5, 2026 6:10pm
workflow-docs Ready Ready Preview, v0 Aug 5, 2026 6:10pm
workflow-swc-playground Ready Ready Preview Aug 5, 2026 6:10pm
workflow-tarballs Ready Ready Preview Aug 5, 2026 6:10pm
workflow-web Ready Ready Preview Aug 5, 2026 6:10pm

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

All tests passed

E2E Test Summary

Summary
Passed Failed Skipped Total
✅ ▲ Vercel Production 2932 0 500 3432
✅ 💻 Local Development 3290 0 454 3744
✅ 📦 Local Production 3290 0 454 3744
✅ 🐘 Local Postgres 3290 0 454 3744
✅ 🪟 Windows 312 0 0 312
✅ 📋 Other 2068 0 428 2496
✅ vercel-multi-region 27 0 0 27
Total 15209 0 2290 17499
Details by Category

✅ ▲ Vercel Production

App Passed Failed Skipped
✅ astro-node 127 0 29
✅ astro-quickjs 127 0 29
✅ example-node 127 0 29
✅ example-quickjs 127 0 29
✅ express-node 127 0 29
✅ express-quickjs 127 0 29
✅ fastify-node 127 0 29
✅ fastify-quickjs 127 0 29
✅ hono-node 127 0 29
✅ hono-quickjs 127 0 29
✅ nextjs-turbopack-node 152 0 4
✅ nextjs-turbopack-quickjs 152 0 4
✅ nextjs-webpack-node 152 0 4
✅ nextjs-webpack-quickjs 152 0 4
✅ nitro-node 127 0 29
✅ nitro-quickjs 127 0 29
✅ nuxt-node 127 0 29
✅ nuxt-quickjs 127 0 29
✅ sveltekit-node 146 0 10
✅ sveltekit-quickjs 146 0 10
✅ vite-node 127 0 29
✅ vite-quickjs 127 0 29

✅ 💻 Local Development

App Passed Failed Skipped
✅ astro-stable-node 130 0 26
✅ astro-stable-quickjs 130 0 26
✅ express-stable-node 130 0 26
✅ express-stable-quickjs 130 0 26
✅ fastify-stable-node 130 0 26
✅ fastify-stable-quickjs 130 0 26
✅ hono-stable-node 130 0 26
✅ hono-stable-quickjs 130 0 26
✅ nextjs-turbopack-canary-node 137 0 19
✅ nextjs-turbopack-canary-quickjs 137 0 19
✅ nextjs-turbopack-stable-node 156 0 0
✅ nextjs-turbopack-stable-quickjs 156 0 0
✅ nextjs-webpack-canary-node 137 0 19
✅ nextjs-webpack-canary-quickjs 137 0 19
✅ nextjs-webpack-stable-node 156 0 0
✅ nextjs-webpack-stable-quickjs 156 0 0
✅ nitro-stable-node 130 0 26
✅ nitro-stable-quickjs 130 0 26
✅ nuxt-stable-node 130 0 26
✅ nuxt-stable-quickjs 130 0 26
✅ sveltekit-stable-node 149 0 7
✅ sveltekit-stable-quickjs 149 0 7
✅ vite-stable-node 130 0 26
✅ vite-stable-quickjs 130 0 26

✅ 📦 Local Production

App Passed Failed Skipped
✅ astro-stable-node 130 0 26
✅ astro-stable-quickjs 130 0 26
✅ express-stable-node 130 0 26
✅ express-stable-quickjs 130 0 26
✅ fastify-stable-node 130 0 26
✅ fastify-stable-quickjs 130 0 26
✅ hono-stable-node 130 0 26
✅ hono-stable-quickjs 130 0 26
✅ nextjs-turbopack-canary-node 137 0 19
✅ nextjs-turbopack-canary-quickjs 137 0 19
✅ nextjs-turbopack-stable-node 156 0 0
✅ nextjs-turbopack-stable-quickjs 156 0 0
✅ nextjs-webpack-canary-node 137 0 19
✅ nextjs-webpack-canary-quickjs 137 0 19
✅ nextjs-webpack-stable-node 156 0 0
✅ nextjs-webpack-stable-quickjs 156 0 0
✅ nitro-stable-node 130 0 26
✅ nitro-stable-quickjs 130 0 26
✅ nuxt-stable-node 130 0 26
✅ nuxt-stable-quickjs 130 0 26
✅ sveltekit-stable-node 149 0 7
✅ sveltekit-stable-quickjs 149 0 7
✅ vite-stable-node 130 0 26
✅ vite-stable-quickjs 130 0 26

✅ 🐘 Local Postgres

App Passed Failed Skipped
✅ astro-stable-node 130 0 26
✅ astro-stable-quickjs 130 0 26
✅ express-stable-node 130 0 26
✅ express-stable-quickjs 130 0 26
✅ fastify-stable-node 130 0 26
✅ fastify-stable-quickjs 130 0 26
✅ hono-stable-node 130 0 26
✅ hono-stable-quickjs 130 0 26
✅ nextjs-turbopack-canary-node 137 0 19
✅ nextjs-turbopack-canary-quickjs 137 0 19
✅ nextjs-turbopack-stable-node 156 0 0
✅ nextjs-turbopack-stable-quickjs 156 0 0
✅ nextjs-webpack-canary-node 137 0 19
✅ nextjs-webpack-canary-quickjs 137 0 19
✅ nextjs-webpack-stable-node 156 0 0
✅ nextjs-webpack-stable-quickjs 156 0 0
✅ nitro-stable-node 130 0 26
✅ nitro-stable-quickjs 130 0 26
✅ nuxt-stable-node 130 0 26
✅ nuxt-stable-quickjs 130 0 26
✅ sveltekit-stable-node 149 0 7
✅ sveltekit-stable-quickjs 149 0 7
✅ vite-stable-node 130 0 26
✅ vite-stable-quickjs 130 0 26

✅ 🪟 Windows

App Passed Failed Skipped
✅ nextjs-turbopack-node 156 0 0
✅ nextjs-turbopack-quickjs 156 0 0

✅ 📋 Other

App Passed Failed Skipped
✅ e2e-local-dev-nest-stable-node 130 0 26
✅ e2e-local-dev-nest-stable-quickjs 130 0 26
✅ e2e-local-dev-tanstack-start-node 130 0 26
✅ e2e-local-dev-tanstack-start-quickjs 130 0 26
✅ e2e-local-postgres-nest-stable-node 130 0 26
✅ e2e-local-postgres-nest-stable-quickjs 130 0 26
✅ e2e-local-postgres-tanstack-start-node 130 0 26
✅ e2e-local-postgres-tanstack-start-quickjs 130 0 26
✅ e2e-local-prod-nest-stable-node 130 0 26
✅ e2e-local-prod-nest-stable-quickjs 130 0 26
✅ e2e-local-prod-tanstack-start-node 130 0 26
✅ e2e-local-prod-tanstack-start-quickjs 130 0 26
✅ e2e-vercel-prod-nest-node 127 0 29
✅ e2e-vercel-prod-nest-quickjs 127 0 29
✅ e2e-vercel-prod-tanstack-start-node 127 0 29
✅ e2e-vercel-prod-tanstack-start-quickjs 127 0 29

✅ vercel-multi-region

App Passed Failed Skipped
✅ nextjs-turbopack 27 0 0

📋 View full workflow run

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

commit 6e402e0 · Wed, 05 Aug 2026 18:25:08 GMT · run logs

Backend: vercel · app: nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 263 (-40%) 💚 1513 🔴 (+34%) 🔻 1691 🔴 (+16%) 🔻 2164 🔴 (+40%) 🔻 30
TTFS stream 244 (+38%) 🔻 1436 🔴 (+35%) 🔻 1460 🔴 (+37%) 🔻 1569 🔴 (+36%) 🔻 30
TTFS hook + stream 361 (-71%) 💚 1682 🔴 (+22%) 🔻 1849 🔴 (+29%) 🔻 2128 🔴 (+27%) 🔻 30
STSO 1020 steps (inline) 74 (-13%) 156 (+12%) 183 (+12%) 344 (+29%) 🔻 1018
STSO 1020 steps (queue-hop) 3886 (+31%) 🔻 3886 (+31%) 🔻 3886 (+31%) 🔻 3886 (+31%) 🔻 1
WO 1020 steps 156389 (+13%) 156389 (+13%) 156389 (+13%) 156389 (+13%) 1
SL stream latency 106 (+26%) 🔻 193 🔴 (+71%) 🔻 234 🔴 (+67%) 🔻 698 🔴 (+92%) 🔻 30
SO stream overhead (text) 124 (+16%) 🔻 247 (+52%) 🔻 561 🔴 (+203%) 🔻 936 (+168%) 🔻 30
SO stream overhead (structured) 155 (+46%) 🔻 270 🔴 (+78%) 🔻 296 (+75%) 🔻 1010 🔴 (+355%) 🔻 30
📈 STSO distribution vs main (inline / queue-hop histograms)

1020 steps (inline)

Cumulative STSO time: main 133947ms → this run 152192ms (Δ +18245ms, +14%)

 50-100 ms  ┃                         main  15  this   2   -13
100-150 ms  ███████████████████┃████  main 827  this 696  -131
150-200 ms  ████░░┃                   main 137  this 252  +115
200-250 ms  ┃                         main  23  this  36   +13
250-300 ms  ┃                         main  10  this  13    +3
300-350 ms  ┃                         main   3  this   9    +6
350-400 ms  ┃                         main   2  this   6    +4
400-450 ms  ┃                         main   0  this   2    +2
500-550 ms  ┃                         main   1  this   0    -1
700-750 ms  ┃                         main   0  this   1    +1
900-950 ms  ┃                         main   0  this   1    +1

1020 steps (queue-hop)

Cumulative STSO time: main 2956ms → this run 3886ms (Δ +930ms, +31%)

2500-3000 ms  ┃███████████████████████  main 1  this 0  -1
3500-4000 ms  ░░░░░░░░░░░░░░░░░░░░░░░┃  main 0  this 1  +1
📜 Previous results (1)

6921d21

Wed, 05 Aug 2026 15:55:06 GMT · run logs

vercel / nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1346 (+52%) 🔻 1499 🔴 (+45%) 🔻 1524 🔴 (+15%) 1757 🔴 (+19%) 🔻 30
TTFS stream 1328 (+36%) 🔻 1413 🔴 (+37%) 🔻 1465 🔴 (+40%) 🔻 1591 🔴 (+37%) 🔻 30
TTFS hook + stream 1288 (+206%) 🔻 1899 🔴 (+46%) 🔻 1926 🔴 (+26%) 🔻 1987 🔴 (+16%) 🔻 30
STSO 1020 steps (inline) 82 (-3.5%) 158 (+13%) 174 (+4.8%) 270 (-37%) 💚 1018
STSO 1020 steps (queue-hop) 3530 (-25%) 💚 3530 (-25%) 💚 3530 (-25%) 💚 3530 (-25%) 💚 1
WO 1020 steps 161450 (+7.9%) 161450 (+7.9%) 161450 (+7.9%) 161450 (+7.9%) 1
SL stream latency 131 (+60%) 🔻 205 🔴 (+90%) 🔻 456 🔴 (+286%) 🔻 4090 🔴 (+2320%) 🔻 30
SO stream overhead (text) 153 (+47%) 🔻 210 (+24%) 🔻 216 (+21%) 🔻 246 (-32%) 💚 30
SO stream overhead (structured) 135 (+30%) 🔻 210 (+19%) 🔻 232 (±0%) 416 (-19%) 💚 30
ℹ️ Metric definitions & methodology

The collapsed STSO distribution section above buckets every step gap of the sequential-steps run (not a sampled window), split by whether the step ending the gap ran inline — in the same warm process as the step before it, so the gap is pure framework overhead — or after a queue-hop — the first step of a fresh process, which pays queue dispatch, client reinit and event-log replay. Bars overlay the two runs: is main, marks where this run lands, bridges the gap when this run has more samples in a bucket.

Best/P75/P90/P99 deltas compare against the most recent benchmark run on main at the time of this run. 🔻 flags a delta worse than +15%, 💚 one better than −15%.

Metrics — TTFS: time to first step body (in-deployment start() → first step body, deployment clocks) · STSO: step-to-step overhead (gap between consecutive step bodies) · WO: workflow overhead (whole-run time outside step bodies, in-deployment anchored) · SL: stream latency (in-deployment write → read propagation, readAt - writtenAt) · SO: stream overhead (end-to-end write+consume time beyond the modelled generation window)

Scenarios — step: one trivial no-op step, no stream; no hooks, so the run stays in turbo mode (in-process fast path) · stream: one streaming step; no hooks, so the run stays in turbo mode (in-process fast path) · hook + stream: registers a hook before one step, which exits turbo mode (dispatch path) · 1020 steps: 1020 trivial sequential steps; STSO is measured between consecutive steps in the given step ranges, and WO is the whole-run overhead outside step bodies · stream latency: parallel reader/writer steps on a dedicated stream; SL is the in-deployment write->read propagation (readAt - writtenAt) · stream overhead (text): writer streams 300 variable-length text token deltas paced at 100/s for 3s (a haiku-size LLM's token throughput) while a parallel reader drains the whole stream; SO is the end-to-end write+consume time beyond the 3s generation window (overhead/backpressure) · stream overhead (structured): same workload as stream overhead (text), but each delta is an AI-SDK-style structured object ({ type: 'text-delta', id, text }) instead of a raw string, so the SO gap vs the text scenario is the added serialization cost

🔴 marks a percentile over its target (within target is left unmarked). Targets (p75/p90/p99, ms) — TTFS 200/300/600 · SL 50/60/125 · SO 250/500/1000

All metrics are measured from deployment-side timestamps only. Runs are triggered by an in-deployment route that stamps the anchor (clientStart) right before start(), so the CI runner’s request and its path through api.vercel.com sit outside every measured window. TTFS = in-deployment start() → first step body (turbo uses the in-process fast path, non-turbo the dispatch path), and includes the VQS dispatch hop plus any /flow cold start. STSO/WO are measured between step bodies on the deployment. SL is measured inside the workflow (parallel reader/writer steps), so it no longer includes the api.vercel.com read path.

Cold starts are kept in the numbers on purpose — they are part of real bursty-workload latency. The workbench deployment cold-starts the /flow invocation for a large fraction of runs, inflating P75+; the Best column shows the fastest (warm-start) sample for comparison.

@alangenfeld
alangenfeld force-pushed the alangenfeld/fail-cross-deployment-runs branch from 1da67fa to 3ef6bcc Compare July 16, 2026 18:25
@alangenfeld
alangenfeld force-pushed the alangenfeld/fail-cross-deployment-runs branch from a359b5e to adba26d Compare July 29, 2026 16:56

@TooTallNate TooTallNate left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at d3ae266 (single commit; base 23h / 12 commits behind main — merges clean, and see the merged-verification note below since #3046 heavily rewrote runtime.ts in the interim).

Verified locally:

  • PR branch: full core suite green (1814 passed / 3 expected fail), errors package green, all 106 guard/classify/describe/runtime tests pass.
  • Test-merged with current main (post-#3046 retained-VM rewrite): build + full core suite green, 1929 passed — both features' suites coexist. The guard sits before the main replay loop (once per delivery), so it has no interaction with per-iteration VM retention.

Adversarial checks that held up:

  • The turbo self-defeat question: for a misrouted turbo initial delivery, this (wrong) deployment materializes the run itself — if run_created/the synthesized snapshot stamped the ambient deployment id, the guard would never fire and the run would be permanently mispinned. Verified it stamps runInput.deploymentId (the producer's pin) in both the resilient-start eventData and the synthesized workflowRun, so the guard trips correctly and the backend record stays pinned right regardless of who wrote it.
  • "Strictly better-addressed" claim: confirmed in world-vercel's queue — the original send inherits ambient VERCEL_DEPLOYMENT_ID (opts?.deploymentId ?? process.env.VERCEL_DEPLOYMENT_ID), and the re-route passes an explicit deploymentId, which the transport also preserves across its own redeliveries.
  • Payload hygiene on re-route: runInput deliberately dropped (re-engaging turbo would wedge the run), hookInput carried (the lazy-hook re-ensure runs after the guard, so the re-routed message can be the only copy — and the re-ensure is idempotent per resumeId), stepId/stepName preserved on the step path, delivery-chain budgets (replayDivergence, serverErrorRetryCount) correctly reset. All four asserted in the runtime integration tests.
  • Bounded: the count rides the message (deploymentMismatchRetryCount, old messages default 0), so ping-ponging across deployments converges at the budget. Non-continue outcomes return-and-ack, and the EntityConflictError/RunExpiredError swallow in failRun keeps that true for terminal races.
  • Unencrypted run_failed payload rationale is sound (pinned deployment's key may be unreachable — the exact failure being handled), SPEC_VERSION_CURRENT on the event matches all four existing run_failed writers, and describe-error checks DEPLOYMENT_MISMATCH before the generic WorkflowRuntimeError branch (the subclass ordering trap was anticipated).
  • Docs: WORKFLOW_DEPLOYMENT_MISMATCH_MAX_RETRIES documented in runtime-tuning ✓; error page added for both v4 and v5 — necessary, since /err/:slug redirects into the default (v4) tree ✓.

Two non-blocking asks:

  1. Changeset bump types: @workflow/errors (new exported WorkflowDeploymentMismatchError + DEPLOYMENT_MISMATCH code/slug) and @workflow/world (new WorldCapabilities.deploymentAffinity field) add public API surface — those two should be minor, not patch. On main the beta numbering doesn't care, but this is exactly the kind of stability fix that gets backported to stable, where the bump type is preserved. (@workflow/core / @workflow/world-vercel as patch are fine.)
  2. Stale CI, please rerun: the astro - stable / sveltekit - stable E2E failures are tarball-staging errors from a run against an outdated merge-base — the job names even predate the current engine-matrix naming, and the equivalent lanes pass today on fresher PRs (e.g. #3329) and in my local merged-with-main run. Rebase or rerun to green the required check; nothing in this diff touches packaging.

A run is pinned to one deployment when it starts — the deployment that
called start(), or whatever start({ deploymentId }) resolved to (an
explicit id or 'latest'). When a queue callback reaches any other
deployment, the per-run encryption key is derived from the wrong master
key and the delivery throws RuntimeDecryptionError before user code runs;
the queue retry callback swallows it, so the run dies as a blank
"exceeded max retries".

A misrouted delivery is not treated as permanent. It is re-enqueued
explicitly addressed to the run's own deployment, which is strictly
better-addressed than the send that misrouted (that one inherited the
producing deployment's ambient id). The run is failed with the new
DEPLOYMENT_MISMATCH error code only once
WORKFLOW_DEPLOYMENT_MISMATCH_MAX_RETRIES (default 3) is spent, mirroring
the bounded recovery replays a replay divergence gets before being
recorded as a corrupted event log. A deployment that cannot be reached at
all fails on the first delivery instead of burning the budget on an
unroutable target.

Both paths that execute a run are covered — queued step executions and
flow replays — and each already holds the run entity for other reasons,
so the guard adds no backend round trip. Nothing at all happens on the
receiving deployment: no workflow code, no step body, no step_started,
and no lazy-resume hook_received re-ensure. Whatever the delivery was
carrying travels with it, so a re-routed step keeps its identity and a
re-routed lazy hook resume keeps its payload — the pinned deployment
re-ensures the same single event, keyed by resumeId.

Recovery attempts create no events, so a run that recovers looks normal.
They are reported on the invocation span
(workflow.deployment.pinned_id, workflow.deployment_mismatch.retry_count,
workflow.deployment_mismatch.recovered) and as a runtime warning, and the
attempt count is carried in the terminal error message. The recovered
attribute distinguishes a delivery a re-route fixed from one that kept
misrouting, so the ratio is queryable rather than only visible once a run
dies.

Gated on the new World capability deploymentAffinity, which world-vercel
declares. Worlds whose deployment id is synthetic or version-tagged (e.g.
dpl_local@<sdk-version>) leave it unset, so a version bump cannot fail
their runs. The terminal failure is recorded without resolving the run's
encryption key, which is fetched from the pinned deployment's API and is
often gone by the time this fires.

Signed-off-by: Alex Langenfeld <alex.langenfeld@vercel.com>
Signed-off-by: Alex Langenfeld <alex.langenfeld@vercel.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

No backport to stable for 79e4c04 (AI decision).

Although motivated by a real defect (a misrouted queue callback derives the wrong encryption key and the run dies as a blank "exceeded max retries"), this lands as substantial new machinery rather than a narrow fix: a new deploymentAffinity World capability, a new optional Queue.isDeploymentUnavailableError interface method, a new WorkflowDeploymentMismatchError class and DEPLOYMENT_MISMATCH run error code, a new WORKFLOW_DEPLOYMENT_MISMATCH_MAX_RETRIES tuning knob, and new docs pages describing the new behavior — hence the minor bumps for @workflow/errors and @workflow/world. It also introduces a new terminal failure path that can fail runs which previously proceeded, which is exactly the kind of behavior change maintenance-line users stayed behind to avoid. The underlying guard/re-route fix is worth considering separately and can be forced through via workflow_dispatch if desired.

To override, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

79e4c044091185e68bbdcc254a86133e54956ad3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants