') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Resilient step dispatch: parallelize step_created writes with queue publishes by TooTallNate · Pull Request #3365 · vercel/workflow · GitHub
Skip to content

Resilient step dispatch: parallelize step_created writes with queue publishes - #3365

Merged
TooTallNate merged 8 commits into
mainfrom
resilient-step-dispatch
Aug 11, 2026
Merged

Resilient step dispatch: parallelize step_created writes with queue publishes#3365
TooTallNate merged 8 commits into
mainfrom
resilient-step-dispatch

Conversation

@TooTallNate

Copy link
Copy Markdown
Member

Summary

Implements resilient step dispatch: when a suspension hands newly created steps to the queue, the runtime publishes each step's execution message in parallel with its step_created event write instead of sequencing them, and the message carries the serialized step input (stepInput). If the direct write fails transiently (429 / 5xx / transport), the queue consumer idempotently re-ensures the step_created event from the message payload before executing — the same durability pattern as resilient start (runInput) and resilient hook resume (hookInput).

Motivation

Traces from a parallelWorkflow fan-out (64 parallel steps) showed the dispatch phase dominating the invocation:

  • 64 step_created writes ran in parallel (~240ms) ✅
  • …but all 61 queue publishes ran strictly serially afterwards (~45ms each, 2.7s total, ~55% of a 4.9s invocation), and only started after every create finished.

Beyond latency, the create-then-publish sequencing meant a transient step_created write failure surfaced as a failed suspension pass and a full orchestrator redelivery.

What changed

@workflow/world

  • WorkflowInvokePayload.stepInput — serialized step input on step-execution messages
  • CreateEventParams.viaStepDispatch — marks the consumer's re-ensure
  • WorldCapabilities.resilientStepDispatch — backend cooperation attestation (see below)

@workflow/core — producers

  • node:vm: the suspension handler fires Promise.allSettled([step_created write, queue publish w/ stepInput]) per eligible step (all steps concurrent); queue failure is fatal (redelivery recovers, same as today), a transient create failure is swallowed and recovered by the consumer. Queued steps are reported back (queuedStepCorrelationIds) so the dispatch pass skips them.
  • quickjs: dispatchPendingOps does the same for overflow steps, and the ineligible fallback path now publishes in parallel too — removing the serial per-step enqueue loop responsible for the 2.7s above.

@workflow/core — consumer

  • On a redelivery (attempt > 1), a stepInput-carrying message idempotently re-ensures step_created (marked viaStepDispatch) in parallel with the run fetch, then executes. First deliveries pay zero overhead: if the producer's write didn't land, the bare step_started rejects with an error every world routes to redelivery, and attempt 2 materializes the step.

Precondition-guard interaction

  • A guard-enforcing backend can 412-reject a step_created from a stale replay; a queue message carrying that step's payload must not let the consumer materialize what the guard rejected. The parallel path therefore requires the backend to attest cooperation via capabilities.resilientStepDispatch (declared by @workflow/world-vercel): the backend revokes a 412-rejected step's in-flight dispatch (its re-ensure is refused and the message acked) and fences a bare step_started on the entity's step name. Without the attestation, guard-enforcing worlds keep today's sequential dispatch. Worlds without the guard (world-local, world-postgres) don't need it.
  • Step dispatch/retry idempotency keys are now step-identity-scoped (correlationId + hashed step name) so a revoked in-flight message for a reassigned correlation id can never dedupe away the corrected schedule's legitimate dispatch. Safe across versions: queue messages are deployment-pinned, so one run never sees two key schemes.

Kill switch: WORKFLOW_RESILIENT_STEP_DISPATCH=0 restores sequential dispatch (documented in runtime-tuning).

Telemetry: workflow.step.resilient_dispatch_recovered (producer) / workflow.step.resilient_dispatch_materialized (consumer) span attributes.

Rollout

The Vercel backend's cooperating half (revocation markers + viaStepDispatch handling + step-name fence) must be deployed before this ships in an SDK release. Against an older backend, the flag is ignored and the guard-window protection is absent — hence the capability gate.

Testing

  • 8 new suspension-handler tests (parallel publish + payload, transient-failure resilience, queue-failure propagation, all eligibility gates incl. the capability lift)
  • 4 new consumer tests (re-ensure on redelivery w/ viaStepDispatch, first-delivery zero-overhead, conflict-as-success, legacy messages)
  • world schema round-trip test for stepInput
  • Full suites green: 1927 core / 96 world / 340 world-vercel

Docs Preview

Pagev5
Runtime tuning (WORKFLOW_RESILIENT_STEP_DISPATCH)/v5/docs/configuration/runtime-tuning#workflow_resilient_step_dispatch (link via the workflow-docs preview once the vercel[bot] comment appears)

…_created + queue publish)
Newly created steps are handed to the queue in parallel with their
step_created event write, with the serialized input carried on the
message (stepInput) so the queue consumer can idempotently re-ensure
the event when the direct write failed transiently — mirroring
resilient start (runInput) and resilient hook resume (hookInput).
- @workflow/world: stepInput on WorkflowInvokePayload,
CreateEventParams.viaStepDispatch, WorldCapabilities.resilientStepDispatch
- core (node:vm): suspension handler publishes eligible steps alongside
their create; the dispatch pass skips them (queuedStepCorrelationIds)
- core (quickjs): dispatchPendingOps does the same for overflow steps;
the ineligible fallback is now published in parallel too (removes the
serial per-step enqueue loop)
- consumer: on a redelivery, a stepInput-carrying message re-ensures
step_created (marked viaStepDispatch) before executing
- under an enforced precondition guard the parallel path requires
backend cooperation (capabilities.resilientStepDispatch, declared by
world-vercel): a 412-rejected step's in-flight dispatch is revoked
server-side and its re-ensure refused
- step dispatch/retry idempotency keys are step-identity-scoped
(cid + hashed step name) so a revoked message for a reassigned
correlation id cannot absorb the corrected schedule's dispatch
- kill switch: WORKFLOW_RESILIENT_STEP_DISPATCH=0
CopilotAI review requested due to automatic review settings August 5, 2026 22:09
@TooTallNate
TooTallNate requested review from a team, fantix and msullivan as code ownersAugust 5, 2026 22:09
@changeset-bot

changeset-botBot commented Aug 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 23d70d9

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

This PR includes changesets to release 20 packages
NameType
@workflow/worldMinor
@workflow/world-vercelMinor
@workflow/coreMinor
@workflow/cliPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@workflow/world-localPatch
@workflow/world-postgresPatch
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/nextPatch
@workflow/nitroPatch
workflowMinor
@workflow/astroPatch
@workflow/nestPatch
@workflow/rollupPatch
@workflow/sveltekitPatch
@workflow/vitePatch
@workflow/nuxtPatch

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

vercelBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreviewAug 11, 2026 6:47pm
example-nextjs-workflow-webpackReadyReadyPreviewAug 11, 2026 6:47pm
example-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-astro-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-express-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-fastify-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-hono-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-nestjs-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-nitro-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-nuxt-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-python-workflowErrorErrorAug 11, 2026 6:47pm
workbench-sveltekit-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-tanstack-start-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workbench-vite-workflowReadyReadyPreviewAug 11, 2026 6:47pm
workflow-docsBuildingBuildingPreview, v0Aug 11, 2026 6:47pm
workflow-swc-playgroundReadyReadyPreviewAug 11, 2026 6:47pm
workflow-tarballsReadyReadyPreviewAug 11, 2026 6:47pm
workflow-webReadyReadyPreviewAug 11, 2026 6:47pm

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR implements resilient step dispatch for Workflow SDK: newly created steps can be queued with their serialized stepInput while the producer concurrently writes the step_created event, enabling the consumer to re-ensure step_created on redelivery when the direct write failed transiently. It also updates dispatch idempotency keys to be step-identity-scoped and adds capability gating + telemetry + docs.

Changes:

  • Add WorkflowInvokePayload.stepInput and supporting world contracts (viaStepDispatch, resilientStepDispatch capability) to enable consumer-side re-ensure of step_created.
  • Parallelize step dispatch in both node VM suspension handling and QuickJS overflow dispatch, carrying serialized step input on the queue message when eligible.
  • Update idempotency keys, telemetry conventions, docs, and add tests + changeset for the new behavior.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
FileDescription
packages/world/src/queue.tsAdds stepInput schema/types to queue payloads for resilient step dispatch.
packages/world/src/queue.test.tsAdds a schema round-trip test for stepInput on step messages.
packages/world/src/interfaces.tsAdds WorldCapabilities.resilientStepDispatch capability flag.
packages/world/src/events.tsAdds CreateEventParams.viaStepDispatch marker for consumer re-ensure writes.
packages/world-vercel/src/index.tsDeclares backend capability support for resilient step dispatch under guard.
packages/world-vercel/src/events.tsThreads viaStepDispatch through event creation metadata.
packages/world-vercel/src/events-v4.tsAdds viaStepDispatch to v4 create-event metadata plumbing.
packages/core/src/telemetry/semantic-conventions.tsAdds semantic convention attributes for resilient dispatch recovered/materialized.
packages/core/src/runtime/suspension-handler.tsImplements parallel create+publish with stepInput payload and reports queued step CIDs.
packages/core/src/runtime/suspension-handler.test.tsAdds tests covering eligibility gates and resilience semantics for step dispatch.
packages/core/src/runtime/quickjs-entrypoint.tsImplements parallel create+publish for overflow steps and updates dispatch idempotency key usage.
packages/core/src/runtime/helpers.tsAdds stepDispatchIdempotencyKey() helper and FNV-1a hashing for key scoping.
packages/core/src/runtime/constants.tsAdds WORKFLOW_RESILIENT_STEP_DISPATCH kill-switch and payload size cap constant.
packages/core/src/runtime.tsConsumer-side re-ensure of step_created from stepInput on redelivery; updates dispatch keys.
packages/core/src/runtime.test.tsAdds tests for consumer re-ensure behavior on redelivery and legacy/no-stepInput messages.
docs/content/docs/v5/configuration/runtime-tuning.mdxDocuments WORKFLOW_RESILIENT_STEP_DISPATCH.
.changeset/resilient-step-dispatch.mdAdds changeset for @workflow/world, @workflow/world-vercel, and @workflow/core.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/world/src/queue.ts
Comment threadpackages/core/src/runtime.ts Outdated
Review feedback: producers only attach stepInput when the dehydrated
input is binary and the queue transport preserves bytes (CBOR), so a
non-binary value means the payload was mangled in transit. Enforcing
Uint8Array in StepDispatchInputSchema fails the message parse instead
of silently writing non-binary data into a step_created, and types the
consumer's re-ensure so the unchecked 'as SerializedData' cast goes
away.
…he resilientStepDispatch capability lift)
Review feedback (two P1s): backend-side revocation bookkeeping cannot
carry the guard's correctness property across the queue side-channel —
- nothing orders a slow guarded create's eventual 412 (the moment the
backend learns the dispatch is poisoned and records the revocation
marker) before the consumer's redelivery re-ensure, so attempt > 1
is a probabilistic mitigation, not a happens-before; and
- a best-effort marker that fails open (Redis loss) cannot back a
capability the SDK treats as a correctness attestation.
Only sequencing the publish after the create gives the message a
happens-after edge over the create's guard verdict, so the guard gate
is now unconditional: worlds that enforce the precondition guard keep
the sequential create-then-publish dispatch. The parallel resilient
path remains for unguarded writes (the quickjs engine everywhere, and
worlds without the guard). Removes WorldCapabilities.resilientStepDispatch
and world-vercel's declaration; the viaStepDispatch flag is kept and
re-documented as advisory (server-side defense-in-depth only).
This also dissolves the reviewed dedupe hazard on the step-identity-
scoped dispatch keys: with no 410-ack path in any real SDK flow, a
message for a never-created step keeps redelivering until an entity
exists, execution always hydrates input from the committed entity
(never the message), and a name-mismatched stale start is skipped by
the server's stepName fence.
… message-size cap
256 KB is the queue's inline-vs-S3 threshold, not a rejection limit
(payloads above it spill to S3-backed storage transparently). The
128 KiB bound is a cost/latency choice — keep step messages on the
inline path rather than paying an S3 double-hop for bytes that already
live in the event log.
Conflict resolutions:
- suspension-handler.ts: main inlined the EventCreator type (explicit
createEvent/createGuarded signatures); kept this branch's added
imports minus the removed type.
- suspension-handler.test.ts / semantic-conventions.ts: additive on
both sides — kept both.
- world-vercel/events.ts: main restructured createEvent around a shared
v4 input object with run_started / hook_received preload branches;
re-applied this branch's viaStepDispatch spread onto the shared input
so all three branches carry it.

@karthikscale3karthikscale3 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed alongside vercel/workflow-server#714. I do not see a correctness blocker in the current implementation; keeping guarded step dispatch sequential removes the serious stale-schedule race. Before merge, please update the PR description and rollout section because they still describe the removed resilientStepDispatch capability lift and imply the server PR must deploy first. I would also add or verify explicit coverage for a deterministic consumer re-ensure failure so an unusable queued message cannot burn all deliveries. The event-log preload optimization can remain a follow-up. Approving.

…ts its create
Durabench parallel sweeps (guard-off, node engine) caught ~4-8% of
fan-out runs stalling one branch for ~306s on the resilient dispatch
path. Root cause: the consumer's step_created re-ensure was gated on
metadata.attempt > 1, but world-vercel's failure-retry path re-enqueues
a FRESH message whose attempt resets to 1 — so when a delivery beat the
producer's parallel step_created write, every fast retry hit the same
'step not found' rejection with attempt 1, and the step only recovered
when the ORIGINAL message's ~300s visibility-timeout redelivery finally
arrived with attempt 2.
The recovery is now in-band and attempt-independent: when a
stepInput-carrying execution rejects with the step-missing signature
(WorkflowWorldError, 404 or the local worlds' message shape), the
consumer materializes the step_created from the message payload and
retries the execution once within the same delivery. The eager
attempt>1 ensure is kept as a round-trip saver on genuine redeliveries.
Sweep effect expected: the 305-306s TTLS outliers disappear while the
resilient path keeps its p50 win (1054ms vs 1425ms at 64 branches).
@TooTallNate

Copy link
Copy Markdown
MemberAuthor

Baseline sweep results + a bug this PR's sweep caught (fixed in 74f7411)

Ran the 4-version × {20, 64, 256}-branch parallel baseline sweep on durabench (node engine, 5.0.0-beta.40 vs this PR, guard on/off, 25 runs/cell, iad1). TTLS ms:

brversionguardp50p75p95max
64baselineon1373161049014929
64this PRon1389163629504914
64baselineoff1425166048995011
64this PRoff10541289305818305918
  • Guard on (Vercel default for node): PR ≡ baseline — no regression, as designed post-review.
  • Guard off (resilient path active): TTLS p50 −26%, TTFS p50 −49% (413ms vs 803ms) — the create∥publish overlap.
  • But ~4–8% of resilient-path runs stalled one branch for ~306s (at every branch count; never on baseline).

Root cause

The consumer's step_created re-ensure was gated on metadata.attempt > 1. When a delivery beats the producer's parallel step_created write, the bare start rejects with workflow step … not found — and world-vercel's failure-retry path re-enqueues a fresh message whose attempt resets to 1, so every fast retry took the same rejection and the recovery gate was unreachable. The step only ran when the original message's ~300s visibility-timeout redelivery finally arrived carrying attempt: 2. (Confirmed in the run logs: Queue handler failed … workflow step step_01KZQ… not found … retrying in 1s, then silence until ~+300s.)

Fix (74f7411)

Recovery is now in-band and attempt-independent: when a stepInput-carrying execution rejects with the step-missing signature, the consumer materializes the step_created from the message payload and retries the execution once within the same delivery. The eager attempt>1 ensure is kept purely as a round-trip saver on genuine redeliveries. Covered by 3 new consumer tests (world-vercel 404 shape, local-world message shape, and no-stepInput propagation).

Re-running the guard-off cells to confirm the outliers are gone.

(Separate finding, not this PR: all versions show a ~17s TTLS cliff at 256 branches — skew p99 ~9.5s. Tracking that as the next TTLS optimization target.)

Conflict resolutions (main landed the SDK side of slot-mode event
identity, specVersion 6 — #3389):
- suspension-handler.ts: import union (main re-introduced EventCreator
and added mergeReportedEvents; kept this branch's resilient-dispatch
imports) and both result fields (queuedStepCorrelationIds +
reportedEventCount).
- suspension-handler.test.ts / runtime.ts: import unions
(slotToEventId/maxEventSlot/settleEventSlotGap alongside this
branch's stepDispatchIdempotencyKey).
@github-actions

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

🟠 Mint-ordered log — 6 fail of 41 total

log=mint-ordered · fence=per-spec

scenariooutcomeeventsvirtreplayviolations
smoke-no-stepscompleted30msok0
smoke-one-stepcompleted60msok0
hook-at-step-startedcompleted120msok0
hook-at-step-completedcompleted120msok0
hook-at-hook-createdcompleted120msok0
deadline-hook-winscompleted71.0hok0
deadline-expirescompleted71.0hok0
long-sleepcompleted1130.0dok0
hook-never-arrivesstalled30msskipped0
step-retries-twicecompleted102.0sok0
parallel-stepscompleted90msok0
hook-on-execution-statecompleted120msok0
peek-hook-before-branchcompleted120msok0
peek-hook-after-branchcompleted120msok0
peek-hook-at-registrationcompleted120msok0
race-hook-before-probecompleted120msok0
race-hook-after-probecompleted120msok0
race-duplicate-deliverycompleted130msok0
attr-hook-before-stepcompleted110msok0
attr-hook-after-stepcompleted110msok0
attr-from-step-bodycompleted130msok0
fork-hook-after-timeoutcompleted141.0mok0
fork-hook-before-timeoutcompleted141.0mok0
count-hook-after-timeoutcompleted171.0mok0
count-hook-before-timeoutcompleted201.0mok0
stale-read-step-count-forkcompleted171.0mMISMATCH1
stale-read-equal-step-countscompleted141.0mMISMATCH1
step-vs-step-forkcompleted120msMISMATCH1
step-vs-step-fork-fencedcompleted120msMISMATCH1
fence-catches-benign-directioncompleted125msok0
in-flight-before-decisioncompleted171.0mMISMATCH1
in-flight-before-decision-countedcompleted201.0mok0
in-flight-after-decisionfailed142.0mMISMATCH1
stale-read-step-count-fork-fencedcompleted201.0mok0
fork-hook-winscompleted131.0mok0
fork-timeout-winscompleted131.0mok0
unclaimed-payload-under-forkcompleted171.0mok0
claimed-payload-under-forkcompleted171.0mok0
writers-independent-step-bodiescompleted120msok0
writers-scripted-tempocompleted120msok0
cancel-mid-stepcancelled70msskipped0

Full trace: world-sim-mint.txt

🟢 Append-only log — 0 fail of 41 total

log=append-only · fence=per-spec

scenariooutcomeeventsvirtreplayviolations
smoke-no-stepscompleted30msok0
smoke-one-stepcompleted60msok0
hook-at-step-startedcompleted120msok0
hook-at-step-completedcompleted120msok0
hook-at-hook-createdcompleted120msok0
deadline-hook-winscompleted71.0hok0
deadline-expirescompleted71.0hok0
long-sleepcompleted1130.0dok0
hook-never-arrivesstalled30msskipped0
step-retries-twicecompleted102.0sok0
parallel-stepscompleted90msok0
hook-on-execution-statecompleted120msok0
peek-hook-before-branchcompleted120msok0
peek-hook-after-branchcompleted120msok0
peek-hook-at-registrationcompleted120msok0
race-hook-before-probecompleted120msok0
race-hook-after-probecompleted120msok0
race-duplicate-deliverycompleted130msok0
attr-hook-before-stepcompleted110msok0
attr-hook-after-stepcompleted110msok0
attr-from-step-bodycompleted130msok0
fork-hook-after-timeoutcompleted141.0mok0
fork-hook-before-timeoutcompleted141.0mok0
count-hook-after-timeoutcompleted171.0mok0
count-hook-before-timeoutcompleted201.0mok0
stale-read-step-count-forkcompleted201.0mok0
stale-read-equal-step-countscompleted141.0mok0
step-vs-step-forkcompleted120msok0
step-vs-step-fork-fencedcompleted120msok0
fence-catches-benign-directioncompleted125msok0
in-flight-before-decisioncompleted171.0mok0
in-flight-before-decision-countedcompleted171.0mok0
in-flight-after-decisioncompleted192.0mok0
stale-read-step-count-fork-fencedcompleted201.0mok0
fork-hook-winscompleted131.0mok0
fork-timeout-winscompleted131.0mok0
unclaimed-payload-under-forkcompleted171.0mok0
claimed-payload-under-forkcompleted171.0mok0
writers-independent-step-bodiescompleted120msok0
writers-scripted-tempocompleted120msok0
cancel-mid-stepcancelled70msskipped0

Full trace: world-sim-append-only.txt

@github-actions

Copy link
Copy Markdown
Contributor

No backport to stable for 7683130 (AI decision).

This is feature work and a latency optimization, not a stability fix: it adds a new stepInput queue payload field, a viaStepDispatch event param, a new WORKFLOW_RESILIENT_STEP_DISPATCH env flag, new telemetry attributes, and a re-keyed step-dispatch idempotency scheme, all carried by a minor changeset. The motivation is cutting ~2.7s of serial queue publishes out of a fan-out dispatch phase, which is a performance improvement rather than a user-visible defect on stable. The in-band recovery for a delivery that beats its step_created write fixes a stall introduced by this very PR's new path, so it has nothing to repair on the maintenance line.

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

7683130461a1a3de16c13be52d8aee96590b3814

Sign up for freeto 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.

3 participants

@TooTallNate@karthikscale3