Skip to content

[core] Pin correlation-id draw order to event-log order - #3700

Merged
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws
Aug 21, 2026
Merged

[core] Pin correlation-id draw order to event-log order#3700
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Aug 20, 2026

Copy link
Copy Markdown
Member

(AI) Runtime refactor pinning correlation-id draw order to event-log order, the scheduler-side alternative to call-site addressing (#3179) for the residual CORRUPTED_EVENT_LOG class that survives #3554. Default on in this PR (WORKFLOW_LOG_ORDER_DRAWS=0 opts out). Based on main, which now carries #3696's blocked-branch scenario: that scenario reproduces the corruption on unfixed code at 6/6 (vercel), 6/6 (local), 3/6 (postgres) attempts, #3179's arm measured clean with call-site ids, and this PR's event-log-race-repro lane measures the draw-order fix with ids left positional.

Problem

Correlation ids are ordinals of one per-run draw sequence, so the id an entity gets depends on every draw before it. #3554 pinned delivery retirement to log order, but a delivery's continuation still needs a path-dependent number of microtask hops to reach its next draw, and the discipline let later deliveries resolve mid-cascade: a deferring delivery yielded exactly one macrotask, a delivery consumed after an earlier barrier deregistered saw an empty gate set and never yielded at all, and two deliveries waiting concurrently broke the tie by timer arrival. Draw order was therefore a function of arrival, and arrival depends on how much log a replay loaded. Two concurrent replays holding different-length dense prefixes could bind one ordinal to two entities (step_ vs wait_ in the five 2026-08-20 production failures), and every later replay dies on an unconsumable event.

What this changes

awaitEarlierDeliveries gains a flag-gated quiescence fixpoint that runs on every branch-deciding delivery, not only deferring ones. A delivery resolves to the workflow only when, over a full (promise-queue drain + macrotask) turn:

  1. no new correlation-id draws were made and no hydration is in flight (every earlier continuation has run as far as it can), and
  2. no lower-index armed barrier is still registered (no committed earlier delivery is still pending — the tie-break that timer order used to decide).

Unarmed buffered hook payloads keep their claim-driven handover (the gatesOn skip is untouched): a claim happens at a body position, which is already a function of the prefix, and gating steps on unclaimed payloads is what the pre-#3554 harness proved loses the ordering entirely once the idle safety net has to fire.

Draw order then becomes a pure function of the dense log: extending the log can only append draws, never renumber them, so concurrent writers' duplicate creates carry identical ids and dedup (#3381) absorbs them.

The context exposes a monotone mintCount as the quiescence progress metric; lightweight test contexts without it degrade to single-turn quiescence.

Tests

  • log-order-draws.test.ts: the blocked-branch production shape through runWorkflow, nothing hardcoded. Flag off (control): a dense prefix ending before a sibling's launch completion rebinds an ordinal from a finalize step to the sibling's watchdog wait. Flag on: every shared correlation id keeps its binding under pairwise extension AND across a sweep of every dense prefix of the log; replay is deterministic per prefix.
  • Full unit suite green in both modes (2213 passed each way), including the delivery-sensitive suites (storm-log-replay, delivery-barrier-coverage, step-delivery-ordering, hook-sleep-interaction, retained-vm-loop, hop-count). No fixture had to be pinned or rewritten: where arrival order was stable, log order agrees with it; the modes differ only where arrival order was load-dependent, which is the bug.
  • delivery-barrier-coverage.test.ts gains a termination pin for the turnstile's riskiest shape: a step delivery spinning above an armed wait that is itself parked behind an unclaimed buffered payload. Termination there rests on resolvesOnItsOwn excluding the spinner from hasParkedCommittedDelivery, which keeps the idle safety net's gate reachable so it can retire the payload and open the chain. The test replays that log in both flag states; a turnstile or idle-predicate change that breaks the argument deadlocks it instead of only hanging e2e lanes.

Costs and open questions

  1. Replay latency: one event-loop turn per branch-deciding delivery, more when continuations genuinely overlap. Review found the original setTimeout(0) turn cost ~1.1ms per delivery on sequential replays (Node clamps it); the turn is now setImmediate (~15-20µs), and a 100-step sequential replay measures 69.4ms flag-on vs 67.8ms flag-off (median of 5). The production storm-log replay is 30.9-31.3s in both modes, the hook e2e subset against a local prod server is timing-identical, and the unit suite total is unchanged. The benchmark lane's numbers on the current head are the remaining authoritative check.
  2. Engine scope: this applies to the default Node VM engine only. WORKFLOW_RUNTIME=quickjs has its own event feed and correlation-id sequence (quickjs-runtime.ts mints via __generateUlid and never touches the barrier registry), so the setting is inert there — and the repro lane measures the node engine.
  3. Live-side residual: this pins replay-driven scheduling. A resolution acted on in-process before its commit lands (and before a lower-slot event committed by another actor) is not reordered by this discipline; the orchestrator's iteration reload makes that window small, and the repro lane measures whether it matters in practice.
  4. Scheme migration: draw order changes for logs written under arrival order, so enabling/disabling with runs in flight is only safe under deployment pinning (true on Vercel via skew protection). Documented in runtime-tuning.mdx.

Relation to #3179

Complementary, not competing: this fixes the schedule (covers waits and every same-kind entity, closing the per-kind residual call-site ids leave), #3179 fixes the identity (robust even if the scheduler drifts again). Either alone should clear the repro; together they are defense in depth. The repro lanes on the three PRs are the comparison.

🤖 Generated with Claude Code

…OW_LOG_ORDER_DRAWS
A branch-deciding delivery now resolves to the workflow only after every
earlier-in-log delivery's continuation has quiesced (no new draws, no
hydration in flight, over a full macrotask turn) and no lower-slot armed
barrier is still undelivered. Draw order becomes a pure function of the
dense event log, stable under prefix extension, so concurrent replays
holding different-length prefixes mint compatible correlation ids and
their duplicate creates dedup instead of colliding (CORRUPTED_EVENT_LOG).
The single post-deferral macrotask yield this replaces was arrival-order:
a later delivery consumed after an earlier barrier deregistered saw an
empty gate set and resolved mid-cascade, and two deliveries quiescing
concurrently broke the tie by timer order. Buffered hook payloads keep
their claim-driven handover (the gatesOn skip is unchanged): a claim is
body-position determined, already a function of the prefix.
Offline regression: the blocked-branch production shape replayed across
every dense prefix, flag off (control, rebinds an ordinal step->wait) and
flag on (every shared id keeps its binding). Full unit suite green in
both modes. Default on for the repro A/B, mirroring the call-site-ids arm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-botBot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8e045a6

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

This PR includes changesets to release 16 packages
NameType
workflowMinor
@workflow/coreMinor
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@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 20, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-nextjs-workflow-webpackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-astro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-express-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-fastify-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-hono-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nestjs-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nitro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nuxt-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-python-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-sveltekit-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-tanstack-start-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-vite-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-docsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-swc-playgroundReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-tarballsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-webReadyReadyPreview, v0Aug 21, 2026 6:34pm

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (241 failed)

astro-node (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

astro-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

example-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

example-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

express-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

express-quickjs (12 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify-node (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

fastify-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

hono-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

hono-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nest-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nest-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-turbopack-node (5 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nextjs-turbopack-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nitro-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nitro-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

sveltekit-node (5 failed):

  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

sveltekit-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

tanstack-start-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

tanstack-start-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-node (8 failed):

  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

⚠️ Flaky E2E Tests (passed on retry)

These tests failed at least once and passed on a retry. A recurring entry here is a real race worth investigating.

24 flaky tests
  • abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook (sveltekit)
  • abortParallelWorkflow: abort cancels all parallel steps (nextjs-webpack)
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously (sveltekit)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (nitro)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (tanstack-start)
  • hookCleanupTestWorkflow - hook token reuse after workflow completion (sveltekit)
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable (sveltekit)
  • Promise.all of disjoint-key writes: every key lands (example)
  • Promise.all of disjoint-key writes: every key lands (nest)
  • Promise.all of disjoint-key writes: every key lands (nuxt)
  • Promise.all of disjoint-key writes: every key lands (tanstack-start)
  • setAttributesInsideStepWorkflow: step-body calls append attributed native events (nextjs-turbopack)
  • setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly (vite)
  • sleepWinsRaceWorkflow (vite)
  • start: initial attributes are seeded on run creation (example)
  • start: initial attributes are seeded on run creation (nextjs-turbopack)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (express)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (fastify)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (nitro)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (tanstack-start)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (express)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (fastify)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-turbopack)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-webpack)

🛠 Infra Events (absorbed by the harness)

Platform anomalies the e2e harness detected and worked around (e.g. a run the queue never picked up, replaced by a fresh run). Clustered timestamps indicate a backend blip; a steady drip indicates a platform issue worth escalating.

  • cold-start-warmup · suite warmup (tanstack-start) · at 18:35:21Z · abandoned wrun_01M0JSNG5YY2EJ38F5CWJ1C0R0
  • run-pickup-stall · hookCleanupTestWorkflow - hook token reuse after workflow completion (nextjs-webpack) · at 18:41:40Z · abandoned wrun_01M0JT1EKJHZM46BTW6J4K101P

E2E Test Summary

Summary
PassedFailedSkippedTotal
❌ ▲ Vercel Production33372417424320
✅ 💻 Local Development392205584480
✅ 📦 Local Production392205584480
✅ 🐘 Local Postgres392205584480
✅ 🪟 Windows32000320
✅ 🌐 Cross-language Conformance90132141
✅ vercel-multi-region270027
Total15459241254818248
Details by Category

❌ ▲ Vercel Production

AppPassedFailedSkipped
❌ astro-node1211128
❌ astro-quickjs123928
❌ example-node123928
❌ example-quickjs1211128
❌ express-node123928
❌ express-quickjs1201228
❌ fastify-node1221028
❌ fastify-quickjs1221028
❌ hono-node125728
❌ hono-quickjs123928
❌ nest-node123928
❌ nest-quickjs1211128
❌ nextjs-turbopack-node15253
❌ nextjs-turbopack-quickjs14893
❌ nextjs-webpack-node14893
❌ nextjs-webpack-quickjs146113
❌ nitro-node125728
❌ nitro-quickjs1211128
❌ nuxt-node124828
❌ nuxt-quickjs1211128
✅ python-node80152
❌ sveltekit-node14659
❌ sveltekit-quickjs140119
❌ tanstack-start-node124828
❌ tanstack-start-quickjs1221028
❌ vite-node124828
❌ vite-quickjs1211128

✅ 💻 Local Development

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 📦 Local Production

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🐘 Local Postgres

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🪟 Windows

AppPassedFailedSkipped
✅ nextjs-turbopack-node16000
✅ nextjs-turbopack-quickjs16000

✅ 🌐 Cross-language Conformance

AppPassedFailedSkipped
✅ python90132

✅ vercel-multi-region

AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

The benchmark run for 8e045a6 failed. See the run logs for details.

No benchmark results were produced.

ℹ️ Metric definitions & methodology

Metrics —

All timestamps are deployment-side; runs are triggered in-deployment, so the CI runner and api.vercel.com sit outside every measured window. TTFS = start() → first step body (includes dispatch + any cold start); Fan-out TTFS/TTLS = first/last step completion of one Promise.all from the same anchor (the gap is the runtime’s fan-out spread); STSO/WO between step bodies; CRTT inside the workflow (excludes the api.vercel.com read path).

Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor.

@VaguelySeriousVaguelySerious added the event-log-race-repro Run the event log race reproduction job label Aug 20, 2026
@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

🟠 world-sim scenario book — 1 fail of 41 total

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.txt

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Event Log Race Repro

  • vercel clean, 20 runs
  • local clean, 20 runs
  • postgres clean, 20 runs

Run History

RunLaneTotalCompleteCorruptStuckOther
08-20 22:40vercel2020000
local2020000
postgres2020000
08-21 16:46vercel
local
postgres
08-21 16:56vercel2020000
local2020000
postgres2020000
08-21 17:56vercel2020000
local2020000
postgres2020000
08-21 18:40vercel2020000
local2020000
postgres2020000
Config

20 runs / step-storm 6, hook-storm 6, blocked-branch 6, hook-sleep 2 / c8 / 6x8 / watchdog 2500ms / step 2200±250ms / stagger 400ms / burst 4000+1200ms / poke 750ms / poke max 64 / timeout 240000ms

A step delivery's log-order turnstile waits on lower-index ARMED barriers,
and an armed wait can be parked behind an unclaimed buffered hook payload
that only the idle-gated safety net can retire. Termination there rests on
the spinning delivery not counting as a parked committed delivery
(resolvesOnItsOwn excludes it), keeping the net's gate reachable. Pin that
shape with a replay in both flag states so a turnstile or idle-predicate
change that deadlocks it fails fast instead of only hanging e2e lanes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@vercelvercelBot 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.

Additional Suggestion:

The generated e2e diagnostics sidecar glob (e2e-diagnostics-*.json) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.

Fix on Vercel

…son`) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.
This commit fixes the issue reported at .gitignore:53
## Status: partially fixed, gitignore rule still needed
The stray artifact `e2e-diagnostics-express-local.json` from this PR was **removed** by the follow-up commit `fd6e18a` ("Remove stray local e2e diagnostics artifact"). That part of the original suggestion is now done.
However, the **root cause remains unaddressed**: there is still no `.gitignore` rule for the sidecar pattern, so the harness will re-stage these files on the next e2e run.
### Evidence
- `git ls-files | grep e2e-diagnostics` no longer lists `e2e-diagnostics-express-local.json` (removed), but **still** lists a pre-existing sidecar `e2e-diagnostics-nextjs-turbopack-vercel.json` — proof this is a recurring accidental-commit pattern.
- `git check-ignore e2e-diagnostics-express-local.json` returned exit `1` (no matching pattern) before this change.
- `writeDiagnosticsSidecar()` in `packages/core/e2e/utils.ts` writes `e2e-diagnostics-<APP_NAME>-<backend>.json` to `process.cwd()` during e2e runs — clearly per-run output, not source.
### Fix
Add an ignore rule to `.gitignore` (file-removal is already handled by `fd6e18a`):
```
# Per-run e2e diagnostics sidecars written to the repo root by the harness
# (writeDiagnosticsSidecar in packages/core/e2e/utils.ts)
e2e-diagnostics-*.json
```
After the change, `git check-ignore e2e-diagnostics-express-local.json` matches (exit `0`), so future runs won't re-stage the sidecar. The already-tracked `e2e-diagnostics-nextjs-turbopack-vercel.json` is left in place (out of scope), though the new rule covers that pattern for any future untracked instances.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
TooTallNate
TooTallNate previously requested changes Aug 21, 2026

@TooTallNateTooTallNate 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.

AI: Request changes. The new runtime-tuning documentation points users to the wrong VM selector. I found no additional blocking code issues.

- Experimental. Pins correlation-ID draw order to event-log order: a branch-deciding delivery (a step result, hook payload, or wait completion) resolves to the workflow only after every earlier-in-log delivery's continuation has fully quiesced, and never ahead of a lower-slot delivery that is committed to happening.
- Without it, a delivery that resolves while an earlier delivery's continuation is still a few microtask hops from its next step/hook/wait call can overtake it on the run's shared correlation-ID sequence. Draw order — and therefore correlation IDs — then depends on how much of the event log a replay had loaded, and two concurrent replays holding different-length prefixes can bind one ID to two different entities, failing the run with `CORRUPTED_EVENT_LOG`.
- Costs one event-loop turn (roughly 15-20 microseconds via `setImmediate`) per branch-deciding delivery during replay, more when continuations genuinely overlap. Measured on a 100-step sequential replay: about 2ms added end to end.
- Only applies to the default Node.js VM engine. `WORKFLOW_RUNTIME=quickjs` has its own event feed and correlation-ID sequence and is unaffected by this setting.

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.

AI: The engine selector is WORKFLOW_VM, as documented below, not WORKFLOW_RUNTIME. As written, users following this guidance will remain on the Node VM while believing they selected QuickJS. Please change this to WORKFLOW_VM=quickjs.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

(AI) Right — fixed in 8e045a6: the line now reads WORKFLOW_VM=quickjs, matching the selector documented in the WORKFLOW_VM section below. Verified WORKFLOW_RUNTIME appears nowhere else in docs or code; my earlier reply on the quickjs-scope thread used the wrong name too, same correction applies there.

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

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

@github-actionsgithub-actionsBot mentioned this pull request Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

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

Labels

event-log-race-reproRun the event log race reproduction job

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@VaguelySerious@TooTallNate@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
[core] Pin correlation-id draw order to event-log order by VaguelySerious · Pull Request #3700 · vercel/workflow · GitHub
Skip to content

[core] Pin correlation-id draw order to event-log order - #3700

Merged
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws
Aug 21, 2026
Merged

[core] Pin correlation-id draw order to event-log order#3700
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Aug 20, 2026

Copy link
Copy Markdown
Member

(AI) Runtime refactor pinning correlation-id draw order to event-log order, the scheduler-side alternative to call-site addressing (#3179) for the residual CORRUPTED_EVENT_LOG class that survives #3554. Default on in this PR (WORKFLOW_LOG_ORDER_DRAWS=0 opts out). Based on main, which now carries #3696's blocked-branch scenario: that scenario reproduces the corruption on unfixed code at 6/6 (vercel), 6/6 (local), 3/6 (postgres) attempts, #3179's arm measured clean with call-site ids, and this PR's event-log-race-repro lane measures the draw-order fix with ids left positional.

Problem

Correlation ids are ordinals of one per-run draw sequence, so the id an entity gets depends on every draw before it. #3554 pinned delivery retirement to log order, but a delivery's continuation still needs a path-dependent number of microtask hops to reach its next draw, and the discipline let later deliveries resolve mid-cascade: a deferring delivery yielded exactly one macrotask, a delivery consumed after an earlier barrier deregistered saw an empty gate set and never yielded at all, and two deliveries waiting concurrently broke the tie by timer arrival. Draw order was therefore a function of arrival, and arrival depends on how much log a replay loaded. Two concurrent replays holding different-length dense prefixes could bind one ordinal to two entities (step_ vs wait_ in the five 2026-08-20 production failures), and every later replay dies on an unconsumable event.

What this changes

awaitEarlierDeliveries gains a flag-gated quiescence fixpoint that runs on every branch-deciding delivery, not only deferring ones. A delivery resolves to the workflow only when, over a full (promise-queue drain + macrotask) turn:

  1. no new correlation-id draws were made and no hydration is in flight (every earlier continuation has run as far as it can), and
  2. no lower-index armed barrier is still registered (no committed earlier delivery is still pending — the tie-break that timer order used to decide).

Unarmed buffered hook payloads keep their claim-driven handover (the gatesOn skip is untouched): a claim happens at a body position, which is already a function of the prefix, and gating steps on unclaimed payloads is what the pre-#3554 harness proved loses the ordering entirely once the idle safety net has to fire.

Draw order then becomes a pure function of the dense log: extending the log can only append draws, never renumber them, so concurrent writers' duplicate creates carry identical ids and dedup (#3381) absorbs them.

The context exposes a monotone mintCount as the quiescence progress metric; lightweight test contexts without it degrade to single-turn quiescence.

Tests

  • log-order-draws.test.ts: the blocked-branch production shape through runWorkflow, nothing hardcoded. Flag off (control): a dense prefix ending before a sibling's launch completion rebinds an ordinal from a finalize step to the sibling's watchdog wait. Flag on: every shared correlation id keeps its binding under pairwise extension AND across a sweep of every dense prefix of the log; replay is deterministic per prefix.
  • Full unit suite green in both modes (2213 passed each way), including the delivery-sensitive suites (storm-log-replay, delivery-barrier-coverage, step-delivery-ordering, hook-sleep-interaction, retained-vm-loop, hop-count). No fixture had to be pinned or rewritten: where arrival order was stable, log order agrees with it; the modes differ only where arrival order was load-dependent, which is the bug.
  • delivery-barrier-coverage.test.ts gains a termination pin for the turnstile's riskiest shape: a step delivery spinning above an armed wait that is itself parked behind an unclaimed buffered payload. Termination there rests on resolvesOnItsOwn excluding the spinner from hasParkedCommittedDelivery, which keeps the idle safety net's gate reachable so it can retire the payload and open the chain. The test replays that log in both flag states; a turnstile or idle-predicate change that breaks the argument deadlocks it instead of only hanging e2e lanes.

Costs and open questions

  1. Replay latency: one event-loop turn per branch-deciding delivery, more when continuations genuinely overlap. Review found the original setTimeout(0) turn cost ~1.1ms per delivery on sequential replays (Node clamps it); the turn is now setImmediate (~15-20µs), and a 100-step sequential replay measures 69.4ms flag-on vs 67.8ms flag-off (median of 5). The production storm-log replay is 30.9-31.3s in both modes, the hook e2e subset against a local prod server is timing-identical, and the unit suite total is unchanged. The benchmark lane's numbers on the current head are the remaining authoritative check.
  2. Engine scope: this applies to the default Node VM engine only. WORKFLOW_RUNTIME=quickjs has its own event feed and correlation-id sequence (quickjs-runtime.ts mints via __generateUlid and never touches the barrier registry), so the setting is inert there — and the repro lane measures the node engine.
  3. Live-side residual: this pins replay-driven scheduling. A resolution acted on in-process before its commit lands (and before a lower-slot event committed by another actor) is not reordered by this discipline; the orchestrator's iteration reload makes that window small, and the repro lane measures whether it matters in practice.
  4. Scheme migration: draw order changes for logs written under arrival order, so enabling/disabling with runs in flight is only safe under deployment pinning (true on Vercel via skew protection). Documented in runtime-tuning.mdx.

Relation to #3179

Complementary, not competing: this fixes the schedule (covers waits and every same-kind entity, closing the per-kind residual call-site ids leave), #3179 fixes the identity (robust even if the scheduler drifts again). Either alone should clear the repro; together they are defense in depth. The repro lanes on the three PRs are the comparison.

🤖 Generated with Claude Code

…OW_LOG_ORDER_DRAWS
A branch-deciding delivery now resolves to the workflow only after every
earlier-in-log delivery's continuation has quiesced (no new draws, no
hydration in flight, over a full macrotask turn) and no lower-slot armed
barrier is still undelivered. Draw order becomes a pure function of the
dense event log, stable under prefix extension, so concurrent replays
holding different-length prefixes mint compatible correlation ids and
their duplicate creates dedup instead of colliding (CORRUPTED_EVENT_LOG).
The single post-deferral macrotask yield this replaces was arrival-order:
a later delivery consumed after an earlier barrier deregistered saw an
empty gate set and resolved mid-cascade, and two deliveries quiescing
concurrently broke the tie by timer order. Buffered hook payloads keep
their claim-driven handover (the gatesOn skip is unchanged): a claim is
body-position determined, already a function of the prefix.
Offline regression: the blocked-branch production shape replayed across
every dense prefix, flag off (control, rebinds an ordinal step->wait) and
flag on (every shared id keeps its binding). Full unit suite green in
both modes. Default on for the repro A/B, mirroring the call-site-ids arm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-botBot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8e045a6

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

This PR includes changesets to release 16 packages
NameType
workflowMinor
@workflow/coreMinor
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@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 20, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-nextjs-workflow-webpackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-astro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-express-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-fastify-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-hono-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nestjs-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nitro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nuxt-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-python-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-sveltekit-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-tanstack-start-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-vite-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-docsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-swc-playgroundReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-tarballsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-webReadyReadyPreview, v0Aug 21, 2026 6:34pm

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (241 failed)

astro-node (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

astro-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

example-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

example-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

express-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

express-quickjs (12 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify-node (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

fastify-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

hono-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

hono-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nest-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nest-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-turbopack-node (5 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nextjs-turbopack-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nitro-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nitro-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

sveltekit-node (5 failed):

  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

sveltekit-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

tanstack-start-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

tanstack-start-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-node (8 failed):

  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

⚠️ Flaky E2E Tests (passed on retry)

These tests failed at least once and passed on a retry. A recurring entry here is a real race worth investigating.

24 flaky tests
  • abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook (sveltekit)
  • abortParallelWorkflow: abort cancels all parallel steps (nextjs-webpack)
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously (sveltekit)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (nitro)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (tanstack-start)
  • hookCleanupTestWorkflow - hook token reuse after workflow completion (sveltekit)
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable (sveltekit)
  • Promise.all of disjoint-key writes: every key lands (example)
  • Promise.all of disjoint-key writes: every key lands (nest)
  • Promise.all of disjoint-key writes: every key lands (nuxt)
  • Promise.all of disjoint-key writes: every key lands (tanstack-start)
  • setAttributesInsideStepWorkflow: step-body calls append attributed native events (nextjs-turbopack)
  • setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly (vite)
  • sleepWinsRaceWorkflow (vite)
  • start: initial attributes are seeded on run creation (example)
  • start: initial attributes are seeded on run creation (nextjs-turbopack)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (express)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (fastify)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (nitro)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (tanstack-start)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (express)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (fastify)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-turbopack)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-webpack)

🛠 Infra Events (absorbed by the harness)

Platform anomalies the e2e harness detected and worked around (e.g. a run the queue never picked up, replaced by a fresh run). Clustered timestamps indicate a backend blip; a steady drip indicates a platform issue worth escalating.

  • cold-start-warmup · suite warmup (tanstack-start) · at 18:35:21Z · abandoned wrun_01M0JSNG5YY2EJ38F5CWJ1C0R0
  • run-pickup-stall · hookCleanupTestWorkflow - hook token reuse after workflow completion (nextjs-webpack) · at 18:41:40Z · abandoned wrun_01M0JT1EKJHZM46BTW6J4K101P

E2E Test Summary

Summary
PassedFailedSkippedTotal
❌ ▲ Vercel Production33372417424320
✅ 💻 Local Development392205584480
✅ 📦 Local Production392205584480
✅ 🐘 Local Postgres392205584480
✅ 🪟 Windows32000320
✅ 🌐 Cross-language Conformance90132141
✅ vercel-multi-region270027
Total15459241254818248
Details by Category

❌ ▲ Vercel Production

AppPassedFailedSkipped
❌ astro-node1211128
❌ astro-quickjs123928
❌ example-node123928
❌ example-quickjs1211128
❌ express-node123928
❌ express-quickjs1201228
❌ fastify-node1221028
❌ fastify-quickjs1221028
❌ hono-node125728
❌ hono-quickjs123928
❌ nest-node123928
❌ nest-quickjs1211128
❌ nextjs-turbopack-node15253
❌ nextjs-turbopack-quickjs14893
❌ nextjs-webpack-node14893
❌ nextjs-webpack-quickjs146113
❌ nitro-node125728
❌ nitro-quickjs1211128
❌ nuxt-node124828
❌ nuxt-quickjs1211128
✅ python-node80152
❌ sveltekit-node14659
❌ sveltekit-quickjs140119
❌ tanstack-start-node124828
❌ tanstack-start-quickjs1221028
❌ vite-node124828
❌ vite-quickjs1211128

✅ 💻 Local Development

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 📦 Local Production

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🐘 Local Postgres

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🪟 Windows

AppPassedFailedSkipped
✅ nextjs-turbopack-node16000
✅ nextjs-turbopack-quickjs16000

✅ 🌐 Cross-language Conformance

AppPassedFailedSkipped
✅ python90132

✅ vercel-multi-region

AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

The benchmark run for 8e045a6 failed. See the run logs for details.

No benchmark results were produced.

ℹ️ Metric definitions & methodology

Metrics —

All timestamps are deployment-side; runs are triggered in-deployment, so the CI runner and api.vercel.com sit outside every measured window. TTFS = start() → first step body (includes dispatch + any cold start); Fan-out TTFS/TTLS = first/last step completion of one Promise.all from the same anchor (the gap is the runtime’s fan-out spread); STSO/WO between step bodies; CRTT inside the workflow (excludes the api.vercel.com read path).

Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor.

@VaguelySeriousVaguelySerious added the event-log-race-repro Run the event log race reproduction job label Aug 20, 2026
@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

🟠 world-sim scenario book — 1 fail of 41 total

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.txt

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Event Log Race Repro

  • vercel clean, 20 runs
  • local clean, 20 runs
  • postgres clean, 20 runs

Run History

RunLaneTotalCompleteCorruptStuckOther
08-20 22:40vercel2020000
local2020000
postgres2020000
08-21 16:46vercel
local
postgres
08-21 16:56vercel2020000
local2020000
postgres2020000
08-21 17:56vercel2020000
local2020000
postgres2020000
08-21 18:40vercel2020000
local2020000
postgres2020000
Config

20 runs / step-storm 6, hook-storm 6, blocked-branch 6, hook-sleep 2 / c8 / 6x8 / watchdog 2500ms / step 2200±250ms / stagger 400ms / burst 4000+1200ms / poke 750ms / poke max 64 / timeout 240000ms

A step delivery's log-order turnstile waits on lower-index ARMED barriers,
and an armed wait can be parked behind an unclaimed buffered hook payload
that only the idle-gated safety net can retire. Termination there rests on
the spinning delivery not counting as a parked committed delivery
(resolvesOnItsOwn excludes it), keeping the net's gate reachable. Pin that
shape with a replay in both flag states so a turnstile or idle-predicate
change that deadlocks it fails fast instead of only hanging e2e lanes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@vercelvercelBot 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.

Additional Suggestion:

The generated e2e diagnostics sidecar glob (e2e-diagnostics-*.json) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.

Fix on Vercel

…son`) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.
This commit fixes the issue reported at .gitignore:53
## Status: partially fixed, gitignore rule still needed
The stray artifact `e2e-diagnostics-express-local.json` from this PR was **removed** by the follow-up commit `fd6e18a` ("Remove stray local e2e diagnostics artifact"). That part of the original suggestion is now done.
However, the **root cause remains unaddressed**: there is still no `.gitignore` rule for the sidecar pattern, so the harness will re-stage these files on the next e2e run.
### Evidence
- `git ls-files | grep e2e-diagnostics` no longer lists `e2e-diagnostics-express-local.json` (removed), but **still** lists a pre-existing sidecar `e2e-diagnostics-nextjs-turbopack-vercel.json` — proof this is a recurring accidental-commit pattern.
- `git check-ignore e2e-diagnostics-express-local.json` returned exit `1` (no matching pattern) before this change.
- `writeDiagnosticsSidecar()` in `packages/core/e2e/utils.ts` writes `e2e-diagnostics-<APP_NAME>-<backend>.json` to `process.cwd()` during e2e runs — clearly per-run output, not source.
### Fix
Add an ignore rule to `.gitignore` (file-removal is already handled by `fd6e18a`):
```
# Per-run e2e diagnostics sidecars written to the repo root by the harness
# (writeDiagnosticsSidecar in packages/core/e2e/utils.ts)
e2e-diagnostics-*.json
```
After the change, `git check-ignore e2e-diagnostics-express-local.json` matches (exit `0`), so future runs won't re-stage the sidecar. The already-tracked `e2e-diagnostics-nextjs-turbopack-vercel.json` is left in place (out of scope), though the new rule covers that pattern for any future untracked instances.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
TooTallNate
TooTallNate previously requested changes Aug 21, 2026

@TooTallNateTooTallNate 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.

AI: Request changes. The new runtime-tuning documentation points users to the wrong VM selector. I found no additional blocking code issues.

- Experimental. Pins correlation-ID draw order to event-log order: a branch-deciding delivery (a step result, hook payload, or wait completion) resolves to the workflow only after every earlier-in-log delivery's continuation has fully quiesced, and never ahead of a lower-slot delivery that is committed to happening.
- Without it, a delivery that resolves while an earlier delivery's continuation is still a few microtask hops from its next step/hook/wait call can overtake it on the run's shared correlation-ID sequence. Draw order — and therefore correlation IDs — then depends on how much of the event log a replay had loaded, and two concurrent replays holding different-length prefixes can bind one ID to two different entities, failing the run with `CORRUPTED_EVENT_LOG`.
- Costs one event-loop turn (roughly 15-20 microseconds via `setImmediate`) per branch-deciding delivery during replay, more when continuations genuinely overlap. Measured on a 100-step sequential replay: about 2ms added end to end.
- Only applies to the default Node.js VM engine. `WORKFLOW_RUNTIME=quickjs` has its own event feed and correlation-ID sequence and is unaffected by this setting.

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.

AI: The engine selector is WORKFLOW_VM, as documented below, not WORKFLOW_RUNTIME. As written, users following this guidance will remain on the Node VM while believing they selected QuickJS. Please change this to WORKFLOW_VM=quickjs.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

(AI) Right — fixed in 8e045a6: the line now reads WORKFLOW_VM=quickjs, matching the selector documented in the WORKFLOW_VM section below. Verified WORKFLOW_RUNTIME appears nowhere else in docs or code; my earlier reply on the quickjs-scope thread used the wrong name too, same correction applies there.

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

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

@github-actionsgithub-actionsBot mentioned this pull request Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

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

Labels

event-log-race-reproRun the event log race reproduction job

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@VaguelySerious@TooTallNate@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [core] Pin correlation-id draw order to event-log order by VaguelySerious · Pull Request #3700 · vercel/workflow · GitHub
Skip to content

[core] Pin correlation-id draw order to event-log order - #3700

Merged
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws
Aug 21, 2026
Merged

[core] Pin correlation-id draw order to event-log order#3700
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Aug 20, 2026

Copy link
Copy Markdown
Member

(AI) Runtime refactor pinning correlation-id draw order to event-log order, the scheduler-side alternative to call-site addressing (#3179) for the residual CORRUPTED_EVENT_LOG class that survives #3554. Default on in this PR (WORKFLOW_LOG_ORDER_DRAWS=0 opts out). Based on main, which now carries #3696's blocked-branch scenario: that scenario reproduces the corruption on unfixed code at 6/6 (vercel), 6/6 (local), 3/6 (postgres) attempts, #3179's arm measured clean with call-site ids, and this PR's event-log-race-repro lane measures the draw-order fix with ids left positional.

Problem

Correlation ids are ordinals of one per-run draw sequence, so the id an entity gets depends on every draw before it. #3554 pinned delivery retirement to log order, but a delivery's continuation still needs a path-dependent number of microtask hops to reach its next draw, and the discipline let later deliveries resolve mid-cascade: a deferring delivery yielded exactly one macrotask, a delivery consumed after an earlier barrier deregistered saw an empty gate set and never yielded at all, and two deliveries waiting concurrently broke the tie by timer arrival. Draw order was therefore a function of arrival, and arrival depends on how much log a replay loaded. Two concurrent replays holding different-length dense prefixes could bind one ordinal to two entities (step_ vs wait_ in the five 2026-08-20 production failures), and every later replay dies on an unconsumable event.

What this changes

awaitEarlierDeliveries gains a flag-gated quiescence fixpoint that runs on every branch-deciding delivery, not only deferring ones. A delivery resolves to the workflow only when, over a full (promise-queue drain + macrotask) turn:

  1. no new correlation-id draws were made and no hydration is in flight (every earlier continuation has run as far as it can), and
  2. no lower-index armed barrier is still registered (no committed earlier delivery is still pending — the tie-break that timer order used to decide).

Unarmed buffered hook payloads keep their claim-driven handover (the gatesOn skip is untouched): a claim happens at a body position, which is already a function of the prefix, and gating steps on unclaimed payloads is what the pre-#3554 harness proved loses the ordering entirely once the idle safety net has to fire.

Draw order then becomes a pure function of the dense log: extending the log can only append draws, never renumber them, so concurrent writers' duplicate creates carry identical ids and dedup (#3381) absorbs them.

The context exposes a monotone mintCount as the quiescence progress metric; lightweight test contexts without it degrade to single-turn quiescence.

Tests

  • log-order-draws.test.ts: the blocked-branch production shape through runWorkflow, nothing hardcoded. Flag off (control): a dense prefix ending before a sibling's launch completion rebinds an ordinal from a finalize step to the sibling's watchdog wait. Flag on: every shared correlation id keeps its binding under pairwise extension AND across a sweep of every dense prefix of the log; replay is deterministic per prefix.
  • Full unit suite green in both modes (2213 passed each way), including the delivery-sensitive suites (storm-log-replay, delivery-barrier-coverage, step-delivery-ordering, hook-sleep-interaction, retained-vm-loop, hop-count). No fixture had to be pinned or rewritten: where arrival order was stable, log order agrees with it; the modes differ only where arrival order was load-dependent, which is the bug.
  • delivery-barrier-coverage.test.ts gains a termination pin for the turnstile's riskiest shape: a step delivery spinning above an armed wait that is itself parked behind an unclaimed buffered payload. Termination there rests on resolvesOnItsOwn excluding the spinner from hasParkedCommittedDelivery, which keeps the idle safety net's gate reachable so it can retire the payload and open the chain. The test replays that log in both flag states; a turnstile or idle-predicate change that breaks the argument deadlocks it instead of only hanging e2e lanes.

Costs and open questions

  1. Replay latency: one event-loop turn per branch-deciding delivery, more when continuations genuinely overlap. Review found the original setTimeout(0) turn cost ~1.1ms per delivery on sequential replays (Node clamps it); the turn is now setImmediate (~15-20µs), and a 100-step sequential replay measures 69.4ms flag-on vs 67.8ms flag-off (median of 5). The production storm-log replay is 30.9-31.3s in both modes, the hook e2e subset against a local prod server is timing-identical, and the unit suite total is unchanged. The benchmark lane's numbers on the current head are the remaining authoritative check.
  2. Engine scope: this applies to the default Node VM engine only. WORKFLOW_RUNTIME=quickjs has its own event feed and correlation-id sequence (quickjs-runtime.ts mints via __generateUlid and never touches the barrier registry), so the setting is inert there — and the repro lane measures the node engine.
  3. Live-side residual: this pins replay-driven scheduling. A resolution acted on in-process before its commit lands (and before a lower-slot event committed by another actor) is not reordered by this discipline; the orchestrator's iteration reload makes that window small, and the repro lane measures whether it matters in practice.
  4. Scheme migration: draw order changes for logs written under arrival order, so enabling/disabling with runs in flight is only safe under deployment pinning (true on Vercel via skew protection). Documented in runtime-tuning.mdx.

Relation to #3179

Complementary, not competing: this fixes the schedule (covers waits and every same-kind entity, closing the per-kind residual call-site ids leave), #3179 fixes the identity (robust even if the scheduler drifts again). Either alone should clear the repro; together they are defense in depth. The repro lanes on the three PRs are the comparison.

🤖 Generated with Claude Code

…OW_LOG_ORDER_DRAWS
A branch-deciding delivery now resolves to the workflow only after every
earlier-in-log delivery's continuation has quiesced (no new draws, no
hydration in flight, over a full macrotask turn) and no lower-slot armed
barrier is still undelivered. Draw order becomes a pure function of the
dense event log, stable under prefix extension, so concurrent replays
holding different-length prefixes mint compatible correlation ids and
their duplicate creates dedup instead of colliding (CORRUPTED_EVENT_LOG).
The single post-deferral macrotask yield this replaces was arrival-order:
a later delivery consumed after an earlier barrier deregistered saw an
empty gate set and resolved mid-cascade, and two deliveries quiescing
concurrently broke the tie by timer order. Buffered hook payloads keep
their claim-driven handover (the gatesOn skip is unchanged): a claim is
body-position determined, already a function of the prefix.
Offline regression: the blocked-branch production shape replayed across
every dense prefix, flag off (control, rebinds an ordinal step->wait) and
flag on (every shared id keeps its binding). Full unit suite green in
both modes. Default on for the repro A/B, mirroring the call-site-ids arm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-botBot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8e045a6

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

This PR includes changesets to release 16 packages
NameType
workflowMinor
@workflow/coreMinor
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@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 20, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-nextjs-workflow-webpackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-astro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-express-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-fastify-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-hono-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nestjs-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nitro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nuxt-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-python-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-sveltekit-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-tanstack-start-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-vite-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-docsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-swc-playgroundReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-tarballsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-webReadyReadyPreview, v0Aug 21, 2026 6:34pm

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (241 failed)

astro-node (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

astro-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

example-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

example-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

express-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

express-quickjs (12 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify-node (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

fastify-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

hono-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

hono-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nest-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nest-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-turbopack-node (5 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nextjs-turbopack-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nitro-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nitro-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

sveltekit-node (5 failed):

  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

sveltekit-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

tanstack-start-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

tanstack-start-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-node (8 failed):

  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

⚠️ Flaky E2E Tests (passed on retry)

These tests failed at least once and passed on a retry. A recurring entry here is a real race worth investigating.

24 flaky tests
  • abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook (sveltekit)
  • abortParallelWorkflow: abort cancels all parallel steps (nextjs-webpack)
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously (sveltekit)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (nitro)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (tanstack-start)
  • hookCleanupTestWorkflow - hook token reuse after workflow completion (sveltekit)
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable (sveltekit)
  • Promise.all of disjoint-key writes: every key lands (example)
  • Promise.all of disjoint-key writes: every key lands (nest)
  • Promise.all of disjoint-key writes: every key lands (nuxt)
  • Promise.all of disjoint-key writes: every key lands (tanstack-start)
  • setAttributesInsideStepWorkflow: step-body calls append attributed native events (nextjs-turbopack)
  • setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly (vite)
  • sleepWinsRaceWorkflow (vite)
  • start: initial attributes are seeded on run creation (example)
  • start: initial attributes are seeded on run creation (nextjs-turbopack)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (express)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (fastify)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (nitro)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (tanstack-start)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (express)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (fastify)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-turbopack)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-webpack)

🛠 Infra Events (absorbed by the harness)

Platform anomalies the e2e harness detected and worked around (e.g. a run the queue never picked up, replaced by a fresh run). Clustered timestamps indicate a backend blip; a steady drip indicates a platform issue worth escalating.

  • cold-start-warmup · suite warmup (tanstack-start) · at 18:35:21Z · abandoned wrun_01M0JSNG5YY2EJ38F5CWJ1C0R0
  • run-pickup-stall · hookCleanupTestWorkflow - hook token reuse after workflow completion (nextjs-webpack) · at 18:41:40Z · abandoned wrun_01M0JT1EKJHZM46BTW6J4K101P

E2E Test Summary

Summary
PassedFailedSkippedTotal
❌ ▲ Vercel Production33372417424320
✅ 💻 Local Development392205584480
✅ 📦 Local Production392205584480
✅ 🐘 Local Postgres392205584480
✅ 🪟 Windows32000320
✅ 🌐 Cross-language Conformance90132141
✅ vercel-multi-region270027
Total15459241254818248
Details by Category

❌ ▲ Vercel Production

AppPassedFailedSkipped
❌ astro-node1211128
❌ astro-quickjs123928
❌ example-node123928
❌ example-quickjs1211128
❌ express-node123928
❌ express-quickjs1201228
❌ fastify-node1221028
❌ fastify-quickjs1221028
❌ hono-node125728
❌ hono-quickjs123928
❌ nest-node123928
❌ nest-quickjs1211128
❌ nextjs-turbopack-node15253
❌ nextjs-turbopack-quickjs14893
❌ nextjs-webpack-node14893
❌ nextjs-webpack-quickjs146113
❌ nitro-node125728
❌ nitro-quickjs1211128
❌ nuxt-node124828
❌ nuxt-quickjs1211128
✅ python-node80152
❌ sveltekit-node14659
❌ sveltekit-quickjs140119
❌ tanstack-start-node124828
❌ tanstack-start-quickjs1221028
❌ vite-node124828
❌ vite-quickjs1211128

✅ 💻 Local Development

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 📦 Local Production

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🐘 Local Postgres

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🪟 Windows

AppPassedFailedSkipped
✅ nextjs-turbopack-node16000
✅ nextjs-turbopack-quickjs16000

✅ 🌐 Cross-language Conformance

AppPassedFailedSkipped
✅ python90132

✅ vercel-multi-region

AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

The benchmark run for 8e045a6 failed. See the run logs for details.

No benchmark results were produced.

ℹ️ Metric definitions & methodology

Metrics —

All timestamps are deployment-side; runs are triggered in-deployment, so the CI runner and api.vercel.com sit outside every measured window. TTFS = start() → first step body (includes dispatch + any cold start); Fan-out TTFS/TTLS = first/last step completion of one Promise.all from the same anchor (the gap is the runtime’s fan-out spread); STSO/WO between step bodies; CRTT inside the workflow (excludes the api.vercel.com read path).

Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor.

@VaguelySeriousVaguelySerious added the event-log-race-repro Run the event log race reproduction job label Aug 20, 2026
@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

🟠 world-sim scenario book — 1 fail of 41 total

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.txt

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Event Log Race Repro

  • vercel clean, 20 runs
  • local clean, 20 runs
  • postgres clean, 20 runs

Run History

RunLaneTotalCompleteCorruptStuckOther
08-20 22:40vercel2020000
local2020000
postgres2020000
08-21 16:46vercel
local
postgres
08-21 16:56vercel2020000
local2020000
postgres2020000
08-21 17:56vercel2020000
local2020000
postgres2020000
08-21 18:40vercel2020000
local2020000
postgres2020000
Config

20 runs / step-storm 6, hook-storm 6, blocked-branch 6, hook-sleep 2 / c8 / 6x8 / watchdog 2500ms / step 2200±250ms / stagger 400ms / burst 4000+1200ms / poke 750ms / poke max 64 / timeout 240000ms

A step delivery's log-order turnstile waits on lower-index ARMED barriers,
and an armed wait can be parked behind an unclaimed buffered hook payload
that only the idle-gated safety net can retire. Termination there rests on
the spinning delivery not counting as a parked committed delivery
(resolvesOnItsOwn excludes it), keeping the net's gate reachable. Pin that
shape with a replay in both flag states so a turnstile or idle-predicate
change that deadlocks it fails fast instead of only hanging e2e lanes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@vercelvercelBot 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.

Additional Suggestion:

The generated e2e diagnostics sidecar glob (e2e-diagnostics-*.json) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.

Fix on Vercel

…son`) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.
This commit fixes the issue reported at .gitignore:53
## Status: partially fixed, gitignore rule still needed
The stray artifact `e2e-diagnostics-express-local.json` from this PR was **removed** by the follow-up commit `fd6e18a` ("Remove stray local e2e diagnostics artifact"). That part of the original suggestion is now done.
However, the **root cause remains unaddressed**: there is still no `.gitignore` rule for the sidecar pattern, so the harness will re-stage these files on the next e2e run.
### Evidence
- `git ls-files | grep e2e-diagnostics` no longer lists `e2e-diagnostics-express-local.json` (removed), but **still** lists a pre-existing sidecar `e2e-diagnostics-nextjs-turbopack-vercel.json` — proof this is a recurring accidental-commit pattern.
- `git check-ignore e2e-diagnostics-express-local.json` returned exit `1` (no matching pattern) before this change.
- `writeDiagnosticsSidecar()` in `packages/core/e2e/utils.ts` writes `e2e-diagnostics-<APP_NAME>-<backend>.json` to `process.cwd()` during e2e runs — clearly per-run output, not source.
### Fix
Add an ignore rule to `.gitignore` (file-removal is already handled by `fd6e18a`):
```
# Per-run e2e diagnostics sidecars written to the repo root by the harness
# (writeDiagnosticsSidecar in packages/core/e2e/utils.ts)
e2e-diagnostics-*.json
```
After the change, `git check-ignore e2e-diagnostics-express-local.json` matches (exit `0`), so future runs won't re-stage the sidecar. The already-tracked `e2e-diagnostics-nextjs-turbopack-vercel.json` is left in place (out of scope), though the new rule covers that pattern for any future untracked instances.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
TooTallNate
TooTallNate previously requested changes Aug 21, 2026

@TooTallNateTooTallNate 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.

AI: Request changes. The new runtime-tuning documentation points users to the wrong VM selector. I found no additional blocking code issues.

- Experimental. Pins correlation-ID draw order to event-log order: a branch-deciding delivery (a step result, hook payload, or wait completion) resolves to the workflow only after every earlier-in-log delivery's continuation has fully quiesced, and never ahead of a lower-slot delivery that is committed to happening.
- Without it, a delivery that resolves while an earlier delivery's continuation is still a few microtask hops from its next step/hook/wait call can overtake it on the run's shared correlation-ID sequence. Draw order — and therefore correlation IDs — then depends on how much of the event log a replay had loaded, and two concurrent replays holding different-length prefixes can bind one ID to two different entities, failing the run with `CORRUPTED_EVENT_LOG`.
- Costs one event-loop turn (roughly 15-20 microseconds via `setImmediate`) per branch-deciding delivery during replay, more when continuations genuinely overlap. Measured on a 100-step sequential replay: about 2ms added end to end.
- Only applies to the default Node.js VM engine. `WORKFLOW_RUNTIME=quickjs` has its own event feed and correlation-ID sequence and is unaffected by this setting.

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.

AI: The engine selector is WORKFLOW_VM, as documented below, not WORKFLOW_RUNTIME. As written, users following this guidance will remain on the Node VM while believing they selected QuickJS. Please change this to WORKFLOW_VM=quickjs.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

(AI) Right — fixed in 8e045a6: the line now reads WORKFLOW_VM=quickjs, matching the selector documented in the WORKFLOW_VM section below. Verified WORKFLOW_RUNTIME appears nowhere else in docs or code; my earlier reply on the quickjs-scope thread used the wrong name too, same correction applies there.

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

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

@github-actionsgithub-actionsBot mentioned this pull request Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

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

Labels

event-log-race-reproRun the event log race reproduction job

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@VaguelySerious@TooTallNate@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', '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('^' + ".*" + ' [core] Pin correlation-id draw order to event-log order by VaguelySerious · Pull Request #3700 · vercel/workflow · GitHub
Skip to content

[core] Pin correlation-id draw order to event-log order - #3700

Merged
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws
Aug 21, 2026
Merged

[core] Pin correlation-id draw order to event-log order#3700
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Aug 20, 2026

Copy link
Copy Markdown
Member

(AI) Runtime refactor pinning correlation-id draw order to event-log order, the scheduler-side alternative to call-site addressing (#3179) for the residual CORRUPTED_EVENT_LOG class that survives #3554. Default on in this PR (WORKFLOW_LOG_ORDER_DRAWS=0 opts out). Based on main, which now carries #3696's blocked-branch scenario: that scenario reproduces the corruption on unfixed code at 6/6 (vercel), 6/6 (local), 3/6 (postgres) attempts, #3179's arm measured clean with call-site ids, and this PR's event-log-race-repro lane measures the draw-order fix with ids left positional.

Problem

Correlation ids are ordinals of one per-run draw sequence, so the id an entity gets depends on every draw before it. #3554 pinned delivery retirement to log order, but a delivery's continuation still needs a path-dependent number of microtask hops to reach its next draw, and the discipline let later deliveries resolve mid-cascade: a deferring delivery yielded exactly one macrotask, a delivery consumed after an earlier barrier deregistered saw an empty gate set and never yielded at all, and two deliveries waiting concurrently broke the tie by timer arrival. Draw order was therefore a function of arrival, and arrival depends on how much log a replay loaded. Two concurrent replays holding different-length dense prefixes could bind one ordinal to two entities (step_ vs wait_ in the five 2026-08-20 production failures), and every later replay dies on an unconsumable event.

What this changes

awaitEarlierDeliveries gains a flag-gated quiescence fixpoint that runs on every branch-deciding delivery, not only deferring ones. A delivery resolves to the workflow only when, over a full (promise-queue drain + macrotask) turn:

  1. no new correlation-id draws were made and no hydration is in flight (every earlier continuation has run as far as it can), and
  2. no lower-index armed barrier is still registered (no committed earlier delivery is still pending — the tie-break that timer order used to decide).

Unarmed buffered hook payloads keep their claim-driven handover (the gatesOn skip is untouched): a claim happens at a body position, which is already a function of the prefix, and gating steps on unclaimed payloads is what the pre-#3554 harness proved loses the ordering entirely once the idle safety net has to fire.

Draw order then becomes a pure function of the dense log: extending the log can only append draws, never renumber them, so concurrent writers' duplicate creates carry identical ids and dedup (#3381) absorbs them.

The context exposes a monotone mintCount as the quiescence progress metric; lightweight test contexts without it degrade to single-turn quiescence.

Tests

  • log-order-draws.test.ts: the blocked-branch production shape through runWorkflow, nothing hardcoded. Flag off (control): a dense prefix ending before a sibling's launch completion rebinds an ordinal from a finalize step to the sibling's watchdog wait. Flag on: every shared correlation id keeps its binding under pairwise extension AND across a sweep of every dense prefix of the log; replay is deterministic per prefix.
  • Full unit suite green in both modes (2213 passed each way), including the delivery-sensitive suites (storm-log-replay, delivery-barrier-coverage, step-delivery-ordering, hook-sleep-interaction, retained-vm-loop, hop-count). No fixture had to be pinned or rewritten: where arrival order was stable, log order agrees with it; the modes differ only where arrival order was load-dependent, which is the bug.
  • delivery-barrier-coverage.test.ts gains a termination pin for the turnstile's riskiest shape: a step delivery spinning above an armed wait that is itself parked behind an unclaimed buffered payload. Termination there rests on resolvesOnItsOwn excluding the spinner from hasParkedCommittedDelivery, which keeps the idle safety net's gate reachable so it can retire the payload and open the chain. The test replays that log in both flag states; a turnstile or idle-predicate change that breaks the argument deadlocks it instead of only hanging e2e lanes.

Costs and open questions

  1. Replay latency: one event-loop turn per branch-deciding delivery, more when continuations genuinely overlap. Review found the original setTimeout(0) turn cost ~1.1ms per delivery on sequential replays (Node clamps it); the turn is now setImmediate (~15-20µs), and a 100-step sequential replay measures 69.4ms flag-on vs 67.8ms flag-off (median of 5). The production storm-log replay is 30.9-31.3s in both modes, the hook e2e subset against a local prod server is timing-identical, and the unit suite total is unchanged. The benchmark lane's numbers on the current head are the remaining authoritative check.
  2. Engine scope: this applies to the default Node VM engine only. WORKFLOW_RUNTIME=quickjs has its own event feed and correlation-id sequence (quickjs-runtime.ts mints via __generateUlid and never touches the barrier registry), so the setting is inert there — and the repro lane measures the node engine.
  3. Live-side residual: this pins replay-driven scheduling. A resolution acted on in-process before its commit lands (and before a lower-slot event committed by another actor) is not reordered by this discipline; the orchestrator's iteration reload makes that window small, and the repro lane measures whether it matters in practice.
  4. Scheme migration: draw order changes for logs written under arrival order, so enabling/disabling with runs in flight is only safe under deployment pinning (true on Vercel via skew protection). Documented in runtime-tuning.mdx.

Relation to #3179

Complementary, not competing: this fixes the schedule (covers waits and every same-kind entity, closing the per-kind residual call-site ids leave), #3179 fixes the identity (robust even if the scheduler drifts again). Either alone should clear the repro; together they are defense in depth. The repro lanes on the three PRs are the comparison.

🤖 Generated with Claude Code

…OW_LOG_ORDER_DRAWS
A branch-deciding delivery now resolves to the workflow only after every
earlier-in-log delivery's continuation has quiesced (no new draws, no
hydration in flight, over a full macrotask turn) and no lower-slot armed
barrier is still undelivered. Draw order becomes a pure function of the
dense event log, stable under prefix extension, so concurrent replays
holding different-length prefixes mint compatible correlation ids and
their duplicate creates dedup instead of colliding (CORRUPTED_EVENT_LOG).
The single post-deferral macrotask yield this replaces was arrival-order:
a later delivery consumed after an earlier barrier deregistered saw an
empty gate set and resolved mid-cascade, and two deliveries quiescing
concurrently broke the tie by timer order. Buffered hook payloads keep
their claim-driven handover (the gatesOn skip is unchanged): a claim is
body-position determined, already a function of the prefix.
Offline regression: the blocked-branch production shape replayed across
every dense prefix, flag off (control, rebinds an ordinal step->wait) and
flag on (every shared id keeps its binding). Full unit suite green in
both modes. Default on for the repro A/B, mirroring the call-site-ids arm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-botBot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8e045a6

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

This PR includes changesets to release 16 packages
NameType
workflowMinor
@workflow/coreMinor
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@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 20, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-nextjs-workflow-webpackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-astro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-express-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-fastify-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-hono-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nestjs-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nitro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nuxt-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-python-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-sveltekit-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-tanstack-start-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-vite-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-docsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-swc-playgroundReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-tarballsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-webReadyReadyPreview, v0Aug 21, 2026 6:34pm

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (241 failed)

astro-node (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

astro-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

example-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

example-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

express-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

express-quickjs (12 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify-node (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

fastify-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

hono-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

hono-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nest-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nest-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-turbopack-node (5 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nextjs-turbopack-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nitro-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nitro-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

sveltekit-node (5 failed):

  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

sveltekit-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

tanstack-start-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

tanstack-start-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-node (8 failed):

  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

⚠️ Flaky E2E Tests (passed on retry)

These tests failed at least once and passed on a retry. A recurring entry here is a real race worth investigating.

24 flaky tests
  • abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook (sveltekit)
  • abortParallelWorkflow: abort cancels all parallel steps (nextjs-webpack)
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously (sveltekit)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (nitro)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (tanstack-start)
  • hookCleanupTestWorkflow - hook token reuse after workflow completion (sveltekit)
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable (sveltekit)
  • Promise.all of disjoint-key writes: every key lands (example)
  • Promise.all of disjoint-key writes: every key lands (nest)
  • Promise.all of disjoint-key writes: every key lands (nuxt)
  • Promise.all of disjoint-key writes: every key lands (tanstack-start)
  • setAttributesInsideStepWorkflow: step-body calls append attributed native events (nextjs-turbopack)
  • setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly (vite)
  • sleepWinsRaceWorkflow (vite)
  • start: initial attributes are seeded on run creation (example)
  • start: initial attributes are seeded on run creation (nextjs-turbopack)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (express)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (fastify)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (nitro)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (tanstack-start)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (express)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (fastify)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-turbopack)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-webpack)

🛠 Infra Events (absorbed by the harness)

Platform anomalies the e2e harness detected and worked around (e.g. a run the queue never picked up, replaced by a fresh run). Clustered timestamps indicate a backend blip; a steady drip indicates a platform issue worth escalating.

  • cold-start-warmup · suite warmup (tanstack-start) · at 18:35:21Z · abandoned wrun_01M0JSNG5YY2EJ38F5CWJ1C0R0
  • run-pickup-stall · hookCleanupTestWorkflow - hook token reuse after workflow completion (nextjs-webpack) · at 18:41:40Z · abandoned wrun_01M0JT1EKJHZM46BTW6J4K101P

E2E Test Summary

Summary
PassedFailedSkippedTotal
❌ ▲ Vercel Production33372417424320
✅ 💻 Local Development392205584480
✅ 📦 Local Production392205584480
✅ 🐘 Local Postgres392205584480
✅ 🪟 Windows32000320
✅ 🌐 Cross-language Conformance90132141
✅ vercel-multi-region270027
Total15459241254818248
Details by Category

❌ ▲ Vercel Production

AppPassedFailedSkipped
❌ astro-node1211128
❌ astro-quickjs123928
❌ example-node123928
❌ example-quickjs1211128
❌ express-node123928
❌ express-quickjs1201228
❌ fastify-node1221028
❌ fastify-quickjs1221028
❌ hono-node125728
❌ hono-quickjs123928
❌ nest-node123928
❌ nest-quickjs1211128
❌ nextjs-turbopack-node15253
❌ nextjs-turbopack-quickjs14893
❌ nextjs-webpack-node14893
❌ nextjs-webpack-quickjs146113
❌ nitro-node125728
❌ nitro-quickjs1211128
❌ nuxt-node124828
❌ nuxt-quickjs1211128
✅ python-node80152
❌ sveltekit-node14659
❌ sveltekit-quickjs140119
❌ tanstack-start-node124828
❌ tanstack-start-quickjs1221028
❌ vite-node124828
❌ vite-quickjs1211128

✅ 💻 Local Development

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 📦 Local Production

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🐘 Local Postgres

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🪟 Windows

AppPassedFailedSkipped
✅ nextjs-turbopack-node16000
✅ nextjs-turbopack-quickjs16000

✅ 🌐 Cross-language Conformance

AppPassedFailedSkipped
✅ python90132

✅ vercel-multi-region

AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

The benchmark run for 8e045a6 failed. See the run logs for details.

No benchmark results were produced.

ℹ️ Metric definitions & methodology

Metrics —

All timestamps are deployment-side; runs are triggered in-deployment, so the CI runner and api.vercel.com sit outside every measured window. TTFS = start() → first step body (includes dispatch + any cold start); Fan-out TTFS/TTLS = first/last step completion of one Promise.all from the same anchor (the gap is the runtime’s fan-out spread); STSO/WO between step bodies; CRTT inside the workflow (excludes the api.vercel.com read path).

Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor.

@VaguelySeriousVaguelySerious added the event-log-race-repro Run the event log race reproduction job label Aug 20, 2026
@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

🟠 world-sim scenario book — 1 fail of 41 total

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.txt

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Event Log Race Repro

  • vercel clean, 20 runs
  • local clean, 20 runs
  • postgres clean, 20 runs

Run History

RunLaneTotalCompleteCorruptStuckOther
08-20 22:40vercel2020000
local2020000
postgres2020000
08-21 16:46vercel
local
postgres
08-21 16:56vercel2020000
local2020000
postgres2020000
08-21 17:56vercel2020000
local2020000
postgres2020000
08-21 18:40vercel2020000
local2020000
postgres2020000
Config

20 runs / step-storm 6, hook-storm 6, blocked-branch 6, hook-sleep 2 / c8 / 6x8 / watchdog 2500ms / step 2200±250ms / stagger 400ms / burst 4000+1200ms / poke 750ms / poke max 64 / timeout 240000ms

A step delivery's log-order turnstile waits on lower-index ARMED barriers,
and an armed wait can be parked behind an unclaimed buffered hook payload
that only the idle-gated safety net can retire. Termination there rests on
the spinning delivery not counting as a parked committed delivery
(resolvesOnItsOwn excludes it), keeping the net's gate reachable. Pin that
shape with a replay in both flag states so a turnstile or idle-predicate
change that deadlocks it fails fast instead of only hanging e2e lanes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@vercelvercelBot 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.

Additional Suggestion:

The generated e2e diagnostics sidecar glob (e2e-diagnostics-*.json) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.

Fix on Vercel

…son`) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.
This commit fixes the issue reported at .gitignore:53
## Status: partially fixed, gitignore rule still needed
The stray artifact `e2e-diagnostics-express-local.json` from this PR was **removed** by the follow-up commit `fd6e18a` ("Remove stray local e2e diagnostics artifact"). That part of the original suggestion is now done.
However, the **root cause remains unaddressed**: there is still no `.gitignore` rule for the sidecar pattern, so the harness will re-stage these files on the next e2e run.
### Evidence
- `git ls-files | grep e2e-diagnostics` no longer lists `e2e-diagnostics-express-local.json` (removed), but **still** lists a pre-existing sidecar `e2e-diagnostics-nextjs-turbopack-vercel.json` — proof this is a recurring accidental-commit pattern.
- `git check-ignore e2e-diagnostics-express-local.json` returned exit `1` (no matching pattern) before this change.
- `writeDiagnosticsSidecar()` in `packages/core/e2e/utils.ts` writes `e2e-diagnostics-<APP_NAME>-<backend>.json` to `process.cwd()` during e2e runs — clearly per-run output, not source.
### Fix
Add an ignore rule to `.gitignore` (file-removal is already handled by `fd6e18a`):
```
# Per-run e2e diagnostics sidecars written to the repo root by the harness
# (writeDiagnosticsSidecar in packages/core/e2e/utils.ts)
e2e-diagnostics-*.json
```
After the change, `git check-ignore e2e-diagnostics-express-local.json` matches (exit `0`), so future runs won't re-stage the sidecar. The already-tracked `e2e-diagnostics-nextjs-turbopack-vercel.json` is left in place (out of scope), though the new rule covers that pattern for any future untracked instances.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
TooTallNate
TooTallNate previously requested changes Aug 21, 2026

@TooTallNateTooTallNate 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.

AI: Request changes. The new runtime-tuning documentation points users to the wrong VM selector. I found no additional blocking code issues.

- Experimental. Pins correlation-ID draw order to event-log order: a branch-deciding delivery (a step result, hook payload, or wait completion) resolves to the workflow only after every earlier-in-log delivery's continuation has fully quiesced, and never ahead of a lower-slot delivery that is committed to happening.
- Without it, a delivery that resolves while an earlier delivery's continuation is still a few microtask hops from its next step/hook/wait call can overtake it on the run's shared correlation-ID sequence. Draw order — and therefore correlation IDs — then depends on how much of the event log a replay had loaded, and two concurrent replays holding different-length prefixes can bind one ID to two different entities, failing the run with `CORRUPTED_EVENT_LOG`.
- Costs one event-loop turn (roughly 15-20 microseconds via `setImmediate`) per branch-deciding delivery during replay, more when continuations genuinely overlap. Measured on a 100-step sequential replay: about 2ms added end to end.
- Only applies to the default Node.js VM engine. `WORKFLOW_RUNTIME=quickjs` has its own event feed and correlation-ID sequence and is unaffected by this setting.

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.

AI: The engine selector is WORKFLOW_VM, as documented below, not WORKFLOW_RUNTIME. As written, users following this guidance will remain on the Node VM while believing they selected QuickJS. Please change this to WORKFLOW_VM=quickjs.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

(AI) Right — fixed in 8e045a6: the line now reads WORKFLOW_VM=quickjs, matching the selector documented in the WORKFLOW_VM section below. Verified WORKFLOW_RUNTIME appears nowhere else in docs or code; my earlier reply on the quickjs-scope thread used the wrong name too, same correction applies there.

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

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

@github-actionsgithub-actionsBot mentioned this pull request Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

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

Labels

event-log-race-reproRun the event log race reproduction job

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@VaguelySerious@TooTallNate@pranaygp
, '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" + ' [core] Pin correlation-id draw order to event-log order by VaguelySerious · Pull Request #3700 · vercel/workflow · GitHub
Skip to content

[core] Pin correlation-id draw order to event-log order - #3700

Merged
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws
Aug 21, 2026
Merged

[core] Pin correlation-id draw order to event-log order#3700
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Aug 20, 2026

Copy link
Copy Markdown
Member

(AI) Runtime refactor pinning correlation-id draw order to event-log order, the scheduler-side alternative to call-site addressing (#3179) for the residual CORRUPTED_EVENT_LOG class that survives #3554. Default on in this PR (WORKFLOW_LOG_ORDER_DRAWS=0 opts out). Based on main, which now carries #3696's blocked-branch scenario: that scenario reproduces the corruption on unfixed code at 6/6 (vercel), 6/6 (local), 3/6 (postgres) attempts, #3179's arm measured clean with call-site ids, and this PR's event-log-race-repro lane measures the draw-order fix with ids left positional.

Problem

Correlation ids are ordinals of one per-run draw sequence, so the id an entity gets depends on every draw before it. #3554 pinned delivery retirement to log order, but a delivery's continuation still needs a path-dependent number of microtask hops to reach its next draw, and the discipline let later deliveries resolve mid-cascade: a deferring delivery yielded exactly one macrotask, a delivery consumed after an earlier barrier deregistered saw an empty gate set and never yielded at all, and two deliveries waiting concurrently broke the tie by timer arrival. Draw order was therefore a function of arrival, and arrival depends on how much log a replay loaded. Two concurrent replays holding different-length dense prefixes could bind one ordinal to two entities (step_ vs wait_ in the five 2026-08-20 production failures), and every later replay dies on an unconsumable event.

What this changes

awaitEarlierDeliveries gains a flag-gated quiescence fixpoint that runs on every branch-deciding delivery, not only deferring ones. A delivery resolves to the workflow only when, over a full (promise-queue drain + macrotask) turn:

  1. no new correlation-id draws were made and no hydration is in flight (every earlier continuation has run as far as it can), and
  2. no lower-index armed barrier is still registered (no committed earlier delivery is still pending — the tie-break that timer order used to decide).

Unarmed buffered hook payloads keep their claim-driven handover (the gatesOn skip is untouched): a claim happens at a body position, which is already a function of the prefix, and gating steps on unclaimed payloads is what the pre-#3554 harness proved loses the ordering entirely once the idle safety net has to fire.

Draw order then becomes a pure function of the dense log: extending the log can only append draws, never renumber them, so concurrent writers' duplicate creates carry identical ids and dedup (#3381) absorbs them.

The context exposes a monotone mintCount as the quiescence progress metric; lightweight test contexts without it degrade to single-turn quiescence.

Tests

  • log-order-draws.test.ts: the blocked-branch production shape through runWorkflow, nothing hardcoded. Flag off (control): a dense prefix ending before a sibling's launch completion rebinds an ordinal from a finalize step to the sibling's watchdog wait. Flag on: every shared correlation id keeps its binding under pairwise extension AND across a sweep of every dense prefix of the log; replay is deterministic per prefix.
  • Full unit suite green in both modes (2213 passed each way), including the delivery-sensitive suites (storm-log-replay, delivery-barrier-coverage, step-delivery-ordering, hook-sleep-interaction, retained-vm-loop, hop-count). No fixture had to be pinned or rewritten: where arrival order was stable, log order agrees with it; the modes differ only where arrival order was load-dependent, which is the bug.
  • delivery-barrier-coverage.test.ts gains a termination pin for the turnstile's riskiest shape: a step delivery spinning above an armed wait that is itself parked behind an unclaimed buffered payload. Termination there rests on resolvesOnItsOwn excluding the spinner from hasParkedCommittedDelivery, which keeps the idle safety net's gate reachable so it can retire the payload and open the chain. The test replays that log in both flag states; a turnstile or idle-predicate change that breaks the argument deadlocks it instead of only hanging e2e lanes.

Costs and open questions

  1. Replay latency: one event-loop turn per branch-deciding delivery, more when continuations genuinely overlap. Review found the original setTimeout(0) turn cost ~1.1ms per delivery on sequential replays (Node clamps it); the turn is now setImmediate (~15-20µs), and a 100-step sequential replay measures 69.4ms flag-on vs 67.8ms flag-off (median of 5). The production storm-log replay is 30.9-31.3s in both modes, the hook e2e subset against a local prod server is timing-identical, and the unit suite total is unchanged. The benchmark lane's numbers on the current head are the remaining authoritative check.
  2. Engine scope: this applies to the default Node VM engine only. WORKFLOW_RUNTIME=quickjs has its own event feed and correlation-id sequence (quickjs-runtime.ts mints via __generateUlid and never touches the barrier registry), so the setting is inert there — and the repro lane measures the node engine.
  3. Live-side residual: this pins replay-driven scheduling. A resolution acted on in-process before its commit lands (and before a lower-slot event committed by another actor) is not reordered by this discipline; the orchestrator's iteration reload makes that window small, and the repro lane measures whether it matters in practice.
  4. Scheme migration: draw order changes for logs written under arrival order, so enabling/disabling with runs in flight is only safe under deployment pinning (true on Vercel via skew protection). Documented in runtime-tuning.mdx.

Relation to #3179

Complementary, not competing: this fixes the schedule (covers waits and every same-kind entity, closing the per-kind residual call-site ids leave), #3179 fixes the identity (robust even if the scheduler drifts again). Either alone should clear the repro; together they are defense in depth. The repro lanes on the three PRs are the comparison.

🤖 Generated with Claude Code

…OW_LOG_ORDER_DRAWS
A branch-deciding delivery now resolves to the workflow only after every
earlier-in-log delivery's continuation has quiesced (no new draws, no
hydration in flight, over a full macrotask turn) and no lower-slot armed
barrier is still undelivered. Draw order becomes a pure function of the
dense event log, stable under prefix extension, so concurrent replays
holding different-length prefixes mint compatible correlation ids and
their duplicate creates dedup instead of colliding (CORRUPTED_EVENT_LOG).
The single post-deferral macrotask yield this replaces was arrival-order:
a later delivery consumed after an earlier barrier deregistered saw an
empty gate set and resolved mid-cascade, and two deliveries quiescing
concurrently broke the tie by timer order. Buffered hook payloads keep
their claim-driven handover (the gatesOn skip is unchanged): a claim is
body-position determined, already a function of the prefix.
Offline regression: the blocked-branch production shape replayed across
every dense prefix, flag off (control, rebinds an ordinal step->wait) and
flag on (every shared id keeps its binding). Full unit suite green in
both modes. Default on for the repro A/B, mirroring the call-site-ids arm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-botBot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8e045a6

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

This PR includes changesets to release 16 packages
NameType
workflowMinor
@workflow/coreMinor
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@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 20, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-nextjs-workflow-webpackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-astro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-express-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-fastify-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-hono-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nestjs-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nitro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nuxt-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-python-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-sveltekit-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-tanstack-start-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-vite-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-docsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-swc-playgroundReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-tarballsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-webReadyReadyPreview, v0Aug 21, 2026 6:34pm

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (241 failed)

astro-node (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

astro-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

example-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

example-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

express-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

express-quickjs (12 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify-node (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

fastify-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

hono-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

hono-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nest-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nest-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-turbopack-node (5 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nextjs-turbopack-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nitro-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nitro-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

sveltekit-node (5 failed):

  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

sveltekit-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

tanstack-start-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

tanstack-start-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-node (8 failed):

  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

⚠️ Flaky E2E Tests (passed on retry)

These tests failed at least once and passed on a retry. A recurring entry here is a real race worth investigating.

24 flaky tests
  • abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook (sveltekit)
  • abortParallelWorkflow: abort cancels all parallel steps (nextjs-webpack)
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously (sveltekit)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (nitro)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (tanstack-start)
  • hookCleanupTestWorkflow - hook token reuse after workflow completion (sveltekit)
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable (sveltekit)
  • Promise.all of disjoint-key writes: every key lands (example)
  • Promise.all of disjoint-key writes: every key lands (nest)
  • Promise.all of disjoint-key writes: every key lands (nuxt)
  • Promise.all of disjoint-key writes: every key lands (tanstack-start)
  • setAttributesInsideStepWorkflow: step-body calls append attributed native events (nextjs-turbopack)
  • setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly (vite)
  • sleepWinsRaceWorkflow (vite)
  • start: initial attributes are seeded on run creation (example)
  • start: initial attributes are seeded on run creation (nextjs-turbopack)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (express)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (fastify)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (nitro)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (tanstack-start)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (express)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (fastify)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-turbopack)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-webpack)

🛠 Infra Events (absorbed by the harness)

Platform anomalies the e2e harness detected and worked around (e.g. a run the queue never picked up, replaced by a fresh run). Clustered timestamps indicate a backend blip; a steady drip indicates a platform issue worth escalating.

  • cold-start-warmup · suite warmup (tanstack-start) · at 18:35:21Z · abandoned wrun_01M0JSNG5YY2EJ38F5CWJ1C0R0
  • run-pickup-stall · hookCleanupTestWorkflow - hook token reuse after workflow completion (nextjs-webpack) · at 18:41:40Z · abandoned wrun_01M0JT1EKJHZM46BTW6J4K101P

E2E Test Summary

Summary
PassedFailedSkippedTotal
❌ ▲ Vercel Production33372417424320
✅ 💻 Local Development392205584480
✅ 📦 Local Production392205584480
✅ 🐘 Local Postgres392205584480
✅ 🪟 Windows32000320
✅ 🌐 Cross-language Conformance90132141
✅ vercel-multi-region270027
Total15459241254818248
Details by Category

❌ ▲ Vercel Production

AppPassedFailedSkipped
❌ astro-node1211128
❌ astro-quickjs123928
❌ example-node123928
❌ example-quickjs1211128
❌ express-node123928
❌ express-quickjs1201228
❌ fastify-node1221028
❌ fastify-quickjs1221028
❌ hono-node125728
❌ hono-quickjs123928
❌ nest-node123928
❌ nest-quickjs1211128
❌ nextjs-turbopack-node15253
❌ nextjs-turbopack-quickjs14893
❌ nextjs-webpack-node14893
❌ nextjs-webpack-quickjs146113
❌ nitro-node125728
❌ nitro-quickjs1211128
❌ nuxt-node124828
❌ nuxt-quickjs1211128
✅ python-node80152
❌ sveltekit-node14659
❌ sveltekit-quickjs140119
❌ tanstack-start-node124828
❌ tanstack-start-quickjs1221028
❌ vite-node124828
❌ vite-quickjs1211128

✅ 💻 Local Development

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 📦 Local Production

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🐘 Local Postgres

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🪟 Windows

AppPassedFailedSkipped
✅ nextjs-turbopack-node16000
✅ nextjs-turbopack-quickjs16000

✅ 🌐 Cross-language Conformance

AppPassedFailedSkipped
✅ python90132

✅ vercel-multi-region

AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

The benchmark run for 8e045a6 failed. See the run logs for details.

No benchmark results were produced.

ℹ️ Metric definitions & methodology

Metrics —

All timestamps are deployment-side; runs are triggered in-deployment, so the CI runner and api.vercel.com sit outside every measured window. TTFS = start() → first step body (includes dispatch + any cold start); Fan-out TTFS/TTLS = first/last step completion of one Promise.all from the same anchor (the gap is the runtime’s fan-out spread); STSO/WO between step bodies; CRTT inside the workflow (excludes the api.vercel.com read path).

Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor.

@VaguelySeriousVaguelySerious added the event-log-race-repro Run the event log race reproduction job label Aug 20, 2026
@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

🟠 world-sim scenario book — 1 fail of 41 total

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.txt

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Event Log Race Repro

  • vercel clean, 20 runs
  • local clean, 20 runs
  • postgres clean, 20 runs

Run History

RunLaneTotalCompleteCorruptStuckOther
08-20 22:40vercel2020000
local2020000
postgres2020000
08-21 16:46vercel
local
postgres
08-21 16:56vercel2020000
local2020000
postgres2020000
08-21 17:56vercel2020000
local2020000
postgres2020000
08-21 18:40vercel2020000
local2020000
postgres2020000
Config

20 runs / step-storm 6, hook-storm 6, blocked-branch 6, hook-sleep 2 / c8 / 6x8 / watchdog 2500ms / step 2200±250ms / stagger 400ms / burst 4000+1200ms / poke 750ms / poke max 64 / timeout 240000ms

A step delivery's log-order turnstile waits on lower-index ARMED barriers,
and an armed wait can be parked behind an unclaimed buffered hook payload
that only the idle-gated safety net can retire. Termination there rests on
the spinning delivery not counting as a parked committed delivery
(resolvesOnItsOwn excludes it), keeping the net's gate reachable. Pin that
shape with a replay in both flag states so a turnstile or idle-predicate
change that deadlocks it fails fast instead of only hanging e2e lanes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@vercelvercelBot 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.

Additional Suggestion:

The generated e2e diagnostics sidecar glob (e2e-diagnostics-*.json) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.

Fix on Vercel

…son`) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.
This commit fixes the issue reported at .gitignore:53
## Status: partially fixed, gitignore rule still needed
The stray artifact `e2e-diagnostics-express-local.json` from this PR was **removed** by the follow-up commit `fd6e18a` ("Remove stray local e2e diagnostics artifact"). That part of the original suggestion is now done.
However, the **root cause remains unaddressed**: there is still no `.gitignore` rule for the sidecar pattern, so the harness will re-stage these files on the next e2e run.
### Evidence
- `git ls-files | grep e2e-diagnostics` no longer lists `e2e-diagnostics-express-local.json` (removed), but **still** lists a pre-existing sidecar `e2e-diagnostics-nextjs-turbopack-vercel.json` — proof this is a recurring accidental-commit pattern.
- `git check-ignore e2e-diagnostics-express-local.json` returned exit `1` (no matching pattern) before this change.
- `writeDiagnosticsSidecar()` in `packages/core/e2e/utils.ts` writes `e2e-diagnostics-<APP_NAME>-<backend>.json` to `process.cwd()` during e2e runs — clearly per-run output, not source.
### Fix
Add an ignore rule to `.gitignore` (file-removal is already handled by `fd6e18a`):
```
# Per-run e2e diagnostics sidecars written to the repo root by the harness
# (writeDiagnosticsSidecar in packages/core/e2e/utils.ts)
e2e-diagnostics-*.json
```
After the change, `git check-ignore e2e-diagnostics-express-local.json` matches (exit `0`), so future runs won't re-stage the sidecar. The already-tracked `e2e-diagnostics-nextjs-turbopack-vercel.json` is left in place (out of scope), though the new rule covers that pattern for any future untracked instances.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
TooTallNate
TooTallNate previously requested changes Aug 21, 2026

@TooTallNateTooTallNate 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.

AI: Request changes. The new runtime-tuning documentation points users to the wrong VM selector. I found no additional blocking code issues.

- Experimental. Pins correlation-ID draw order to event-log order: a branch-deciding delivery (a step result, hook payload, or wait completion) resolves to the workflow only after every earlier-in-log delivery's continuation has fully quiesced, and never ahead of a lower-slot delivery that is committed to happening.
- Without it, a delivery that resolves while an earlier delivery's continuation is still a few microtask hops from its next step/hook/wait call can overtake it on the run's shared correlation-ID sequence. Draw order — and therefore correlation IDs — then depends on how much of the event log a replay had loaded, and two concurrent replays holding different-length prefixes can bind one ID to two different entities, failing the run with `CORRUPTED_EVENT_LOG`.
- Costs one event-loop turn (roughly 15-20 microseconds via `setImmediate`) per branch-deciding delivery during replay, more when continuations genuinely overlap. Measured on a 100-step sequential replay: about 2ms added end to end.
- Only applies to the default Node.js VM engine. `WORKFLOW_RUNTIME=quickjs` has its own event feed and correlation-ID sequence and is unaffected by this setting.

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.

AI: The engine selector is WORKFLOW_VM, as documented below, not WORKFLOW_RUNTIME. As written, users following this guidance will remain on the Node VM while believing they selected QuickJS. Please change this to WORKFLOW_VM=quickjs.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

(AI) Right — fixed in 8e045a6: the line now reads WORKFLOW_VM=quickjs, matching the selector documented in the WORKFLOW_VM section below. Verified WORKFLOW_RUNTIME appears nowhere else in docs or code; my earlier reply on the quickjs-scope thread used the wrong name too, same correction applies there.

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

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

@github-actionsgithub-actionsBot mentioned this pull request Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

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

Labels

event-log-race-reproRun the event log race reproduction job

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@VaguelySerious@TooTallNate@pranaygp
, '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('^' + ".*" + ' [core] Pin correlation-id draw order to event-log order by VaguelySerious · Pull Request #3700 · vercel/workflow · GitHub
Skip to content

[core] Pin correlation-id draw order to event-log order - #3700

Merged
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws
Aug 21, 2026
Merged

[core] Pin correlation-id draw order to event-log order#3700
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Aug 20, 2026

Copy link
Copy Markdown
Member

(AI) Runtime refactor pinning correlation-id draw order to event-log order, the scheduler-side alternative to call-site addressing (#3179) for the residual CORRUPTED_EVENT_LOG class that survives #3554. Default on in this PR (WORKFLOW_LOG_ORDER_DRAWS=0 opts out). Based on main, which now carries #3696's blocked-branch scenario: that scenario reproduces the corruption on unfixed code at 6/6 (vercel), 6/6 (local), 3/6 (postgres) attempts, #3179's arm measured clean with call-site ids, and this PR's event-log-race-repro lane measures the draw-order fix with ids left positional.

Problem

Correlation ids are ordinals of one per-run draw sequence, so the id an entity gets depends on every draw before it. #3554 pinned delivery retirement to log order, but a delivery's continuation still needs a path-dependent number of microtask hops to reach its next draw, and the discipline let later deliveries resolve mid-cascade: a deferring delivery yielded exactly one macrotask, a delivery consumed after an earlier barrier deregistered saw an empty gate set and never yielded at all, and two deliveries waiting concurrently broke the tie by timer arrival. Draw order was therefore a function of arrival, and arrival depends on how much log a replay loaded. Two concurrent replays holding different-length dense prefixes could bind one ordinal to two entities (step_ vs wait_ in the five 2026-08-20 production failures), and every later replay dies on an unconsumable event.

What this changes

awaitEarlierDeliveries gains a flag-gated quiescence fixpoint that runs on every branch-deciding delivery, not only deferring ones. A delivery resolves to the workflow only when, over a full (promise-queue drain + macrotask) turn:

  1. no new correlation-id draws were made and no hydration is in flight (every earlier continuation has run as far as it can), and
  2. no lower-index armed barrier is still registered (no committed earlier delivery is still pending — the tie-break that timer order used to decide).

Unarmed buffered hook payloads keep their claim-driven handover (the gatesOn skip is untouched): a claim happens at a body position, which is already a function of the prefix, and gating steps on unclaimed payloads is what the pre-#3554 harness proved loses the ordering entirely once the idle safety net has to fire.

Draw order then becomes a pure function of the dense log: extending the log can only append draws, never renumber them, so concurrent writers' duplicate creates carry identical ids and dedup (#3381) absorbs them.

The context exposes a monotone mintCount as the quiescence progress metric; lightweight test contexts without it degrade to single-turn quiescence.

Tests

  • log-order-draws.test.ts: the blocked-branch production shape through runWorkflow, nothing hardcoded. Flag off (control): a dense prefix ending before a sibling's launch completion rebinds an ordinal from a finalize step to the sibling's watchdog wait. Flag on: every shared correlation id keeps its binding under pairwise extension AND across a sweep of every dense prefix of the log; replay is deterministic per prefix.
  • Full unit suite green in both modes (2213 passed each way), including the delivery-sensitive suites (storm-log-replay, delivery-barrier-coverage, step-delivery-ordering, hook-sleep-interaction, retained-vm-loop, hop-count). No fixture had to be pinned or rewritten: where arrival order was stable, log order agrees with it; the modes differ only where arrival order was load-dependent, which is the bug.
  • delivery-barrier-coverage.test.ts gains a termination pin for the turnstile's riskiest shape: a step delivery spinning above an armed wait that is itself parked behind an unclaimed buffered payload. Termination there rests on resolvesOnItsOwn excluding the spinner from hasParkedCommittedDelivery, which keeps the idle safety net's gate reachable so it can retire the payload and open the chain. The test replays that log in both flag states; a turnstile or idle-predicate change that breaks the argument deadlocks it instead of only hanging e2e lanes.

Costs and open questions

  1. Replay latency: one event-loop turn per branch-deciding delivery, more when continuations genuinely overlap. Review found the original setTimeout(0) turn cost ~1.1ms per delivery on sequential replays (Node clamps it); the turn is now setImmediate (~15-20µs), and a 100-step sequential replay measures 69.4ms flag-on vs 67.8ms flag-off (median of 5). The production storm-log replay is 30.9-31.3s in both modes, the hook e2e subset against a local prod server is timing-identical, and the unit suite total is unchanged. The benchmark lane's numbers on the current head are the remaining authoritative check.
  2. Engine scope: this applies to the default Node VM engine only. WORKFLOW_RUNTIME=quickjs has its own event feed and correlation-id sequence (quickjs-runtime.ts mints via __generateUlid and never touches the barrier registry), so the setting is inert there — and the repro lane measures the node engine.
  3. Live-side residual: this pins replay-driven scheduling. A resolution acted on in-process before its commit lands (and before a lower-slot event committed by another actor) is not reordered by this discipline; the orchestrator's iteration reload makes that window small, and the repro lane measures whether it matters in practice.
  4. Scheme migration: draw order changes for logs written under arrival order, so enabling/disabling with runs in flight is only safe under deployment pinning (true on Vercel via skew protection). Documented in runtime-tuning.mdx.

Relation to #3179

Complementary, not competing: this fixes the schedule (covers waits and every same-kind entity, closing the per-kind residual call-site ids leave), #3179 fixes the identity (robust even if the scheduler drifts again). Either alone should clear the repro; together they are defense in depth. The repro lanes on the three PRs are the comparison.

🤖 Generated with Claude Code

…OW_LOG_ORDER_DRAWS
A branch-deciding delivery now resolves to the workflow only after every
earlier-in-log delivery's continuation has quiesced (no new draws, no
hydration in flight, over a full macrotask turn) and no lower-slot armed
barrier is still undelivered. Draw order becomes a pure function of the
dense event log, stable under prefix extension, so concurrent replays
holding different-length prefixes mint compatible correlation ids and
their duplicate creates dedup instead of colliding (CORRUPTED_EVENT_LOG).
The single post-deferral macrotask yield this replaces was arrival-order:
a later delivery consumed after an earlier barrier deregistered saw an
empty gate set and resolved mid-cascade, and two deliveries quiescing
concurrently broke the tie by timer order. Buffered hook payloads keep
their claim-driven handover (the gatesOn skip is unchanged): a claim is
body-position determined, already a function of the prefix.
Offline regression: the blocked-branch production shape replayed across
every dense prefix, flag off (control, rebinds an ordinal step->wait) and
flag on (every shared id keeps its binding). Full unit suite green in
both modes. Default on for the repro A/B, mirroring the call-site-ids arm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-botBot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8e045a6

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

This PR includes changesets to release 16 packages
NameType
workflowMinor
@workflow/coreMinor
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@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 20, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-nextjs-workflow-webpackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-astro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-express-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-fastify-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-hono-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nestjs-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nitro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nuxt-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-python-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-sveltekit-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-tanstack-start-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-vite-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-docsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-swc-playgroundReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-tarballsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-webReadyReadyPreview, v0Aug 21, 2026 6:34pm

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (241 failed)

astro-node (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

astro-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

example-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

example-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

express-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

express-quickjs (12 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify-node (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

fastify-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

hono-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

hono-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nest-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nest-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-turbopack-node (5 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nextjs-turbopack-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nitro-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nitro-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

sveltekit-node (5 failed):

  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

sveltekit-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

tanstack-start-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

tanstack-start-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-node (8 failed):

  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

⚠️ Flaky E2E Tests (passed on retry)

These tests failed at least once and passed on a retry. A recurring entry here is a real race worth investigating.

24 flaky tests
  • abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook (sveltekit)
  • abortParallelWorkflow: abort cancels all parallel steps (nextjs-webpack)
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously (sveltekit)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (nitro)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (tanstack-start)
  • hookCleanupTestWorkflow - hook token reuse after workflow completion (sveltekit)
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable (sveltekit)
  • Promise.all of disjoint-key writes: every key lands (example)
  • Promise.all of disjoint-key writes: every key lands (nest)
  • Promise.all of disjoint-key writes: every key lands (nuxt)
  • Promise.all of disjoint-key writes: every key lands (tanstack-start)
  • setAttributesInsideStepWorkflow: step-body calls append attributed native events (nextjs-turbopack)
  • setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly (vite)
  • sleepWinsRaceWorkflow (vite)
  • start: initial attributes are seeded on run creation (example)
  • start: initial attributes are seeded on run creation (nextjs-turbopack)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (express)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (fastify)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (nitro)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (tanstack-start)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (express)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (fastify)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-turbopack)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-webpack)

🛠 Infra Events (absorbed by the harness)

Platform anomalies the e2e harness detected and worked around (e.g. a run the queue never picked up, replaced by a fresh run). Clustered timestamps indicate a backend blip; a steady drip indicates a platform issue worth escalating.

  • cold-start-warmup · suite warmup (tanstack-start) · at 18:35:21Z · abandoned wrun_01M0JSNG5YY2EJ38F5CWJ1C0R0
  • run-pickup-stall · hookCleanupTestWorkflow - hook token reuse after workflow completion (nextjs-webpack) · at 18:41:40Z · abandoned wrun_01M0JT1EKJHZM46BTW6J4K101P

E2E Test Summary

Summary
PassedFailedSkippedTotal
❌ ▲ Vercel Production33372417424320
✅ 💻 Local Development392205584480
✅ 📦 Local Production392205584480
✅ 🐘 Local Postgres392205584480
✅ 🪟 Windows32000320
✅ 🌐 Cross-language Conformance90132141
✅ vercel-multi-region270027
Total15459241254818248
Details by Category

❌ ▲ Vercel Production

AppPassedFailedSkipped
❌ astro-node1211128
❌ astro-quickjs123928
❌ example-node123928
❌ example-quickjs1211128
❌ express-node123928
❌ express-quickjs1201228
❌ fastify-node1221028
❌ fastify-quickjs1221028
❌ hono-node125728
❌ hono-quickjs123928
❌ nest-node123928
❌ nest-quickjs1211128
❌ nextjs-turbopack-node15253
❌ nextjs-turbopack-quickjs14893
❌ nextjs-webpack-node14893
❌ nextjs-webpack-quickjs146113
❌ nitro-node125728
❌ nitro-quickjs1211128
❌ nuxt-node124828
❌ nuxt-quickjs1211128
✅ python-node80152
❌ sveltekit-node14659
❌ sveltekit-quickjs140119
❌ tanstack-start-node124828
❌ tanstack-start-quickjs1221028
❌ vite-node124828
❌ vite-quickjs1211128

✅ 💻 Local Development

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 📦 Local Production

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🐘 Local Postgres

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🪟 Windows

AppPassedFailedSkipped
✅ nextjs-turbopack-node16000
✅ nextjs-turbopack-quickjs16000

✅ 🌐 Cross-language Conformance

AppPassedFailedSkipped
✅ python90132

✅ vercel-multi-region

AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

The benchmark run for 8e045a6 failed. See the run logs for details.

No benchmark results were produced.

ℹ️ Metric definitions & methodology

Metrics —

All timestamps are deployment-side; runs are triggered in-deployment, so the CI runner and api.vercel.com sit outside every measured window. TTFS = start() → first step body (includes dispatch + any cold start); Fan-out TTFS/TTLS = first/last step completion of one Promise.all from the same anchor (the gap is the runtime’s fan-out spread); STSO/WO between step bodies; CRTT inside the workflow (excludes the api.vercel.com read path).

Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor.

@VaguelySeriousVaguelySerious added the event-log-race-repro Run the event log race reproduction job label Aug 20, 2026
@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

🟠 world-sim scenario book — 1 fail of 41 total

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.txt

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Event Log Race Repro

  • vercel clean, 20 runs
  • local clean, 20 runs
  • postgres clean, 20 runs

Run History

RunLaneTotalCompleteCorruptStuckOther
08-20 22:40vercel2020000
local2020000
postgres2020000
08-21 16:46vercel
local
postgres
08-21 16:56vercel2020000
local2020000
postgres2020000
08-21 17:56vercel2020000
local2020000
postgres2020000
08-21 18:40vercel2020000
local2020000
postgres2020000
Config

20 runs / step-storm 6, hook-storm 6, blocked-branch 6, hook-sleep 2 / c8 / 6x8 / watchdog 2500ms / step 2200±250ms / stagger 400ms / burst 4000+1200ms / poke 750ms / poke max 64 / timeout 240000ms

A step delivery's log-order turnstile waits on lower-index ARMED barriers,
and an armed wait can be parked behind an unclaimed buffered hook payload
that only the idle-gated safety net can retire. Termination there rests on
the spinning delivery not counting as a parked committed delivery
(resolvesOnItsOwn excludes it), keeping the net's gate reachable. Pin that
shape with a replay in both flag states so a turnstile or idle-predicate
change that deadlocks it fails fast instead of only hanging e2e lanes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@vercelvercelBot 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.

Additional Suggestion:

The generated e2e diagnostics sidecar glob (e2e-diagnostics-*.json) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.

Fix on Vercel

…son`) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.
This commit fixes the issue reported at .gitignore:53
## Status: partially fixed, gitignore rule still needed
The stray artifact `e2e-diagnostics-express-local.json` from this PR was **removed** by the follow-up commit `fd6e18a` ("Remove stray local e2e diagnostics artifact"). That part of the original suggestion is now done.
However, the **root cause remains unaddressed**: there is still no `.gitignore` rule for the sidecar pattern, so the harness will re-stage these files on the next e2e run.
### Evidence
- `git ls-files | grep e2e-diagnostics` no longer lists `e2e-diagnostics-express-local.json` (removed), but **still** lists a pre-existing sidecar `e2e-diagnostics-nextjs-turbopack-vercel.json` — proof this is a recurring accidental-commit pattern.
- `git check-ignore e2e-diagnostics-express-local.json` returned exit `1` (no matching pattern) before this change.
- `writeDiagnosticsSidecar()` in `packages/core/e2e/utils.ts` writes `e2e-diagnostics-<APP_NAME>-<backend>.json` to `process.cwd()` during e2e runs — clearly per-run output, not source.
### Fix
Add an ignore rule to `.gitignore` (file-removal is already handled by `fd6e18a`):
```
# Per-run e2e diagnostics sidecars written to the repo root by the harness
# (writeDiagnosticsSidecar in packages/core/e2e/utils.ts)
e2e-diagnostics-*.json
```
After the change, `git check-ignore e2e-diagnostics-express-local.json` matches (exit `0`), so future runs won't re-stage the sidecar. The already-tracked `e2e-diagnostics-nextjs-turbopack-vercel.json` is left in place (out of scope), though the new rule covers that pattern for any future untracked instances.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
TooTallNate
TooTallNate previously requested changes Aug 21, 2026

@TooTallNateTooTallNate 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.

AI: Request changes. The new runtime-tuning documentation points users to the wrong VM selector. I found no additional blocking code issues.

- Experimental. Pins correlation-ID draw order to event-log order: a branch-deciding delivery (a step result, hook payload, or wait completion) resolves to the workflow only after every earlier-in-log delivery's continuation has fully quiesced, and never ahead of a lower-slot delivery that is committed to happening.
- Without it, a delivery that resolves while an earlier delivery's continuation is still a few microtask hops from its next step/hook/wait call can overtake it on the run's shared correlation-ID sequence. Draw order — and therefore correlation IDs — then depends on how much of the event log a replay had loaded, and two concurrent replays holding different-length prefixes can bind one ID to two different entities, failing the run with `CORRUPTED_EVENT_LOG`.
- Costs one event-loop turn (roughly 15-20 microseconds via `setImmediate`) per branch-deciding delivery during replay, more when continuations genuinely overlap. Measured on a 100-step sequential replay: about 2ms added end to end.
- Only applies to the default Node.js VM engine. `WORKFLOW_RUNTIME=quickjs` has its own event feed and correlation-ID sequence and is unaffected by this setting.

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.

AI: The engine selector is WORKFLOW_VM, as documented below, not WORKFLOW_RUNTIME. As written, users following this guidance will remain on the Node VM while believing they selected QuickJS. Please change this to WORKFLOW_VM=quickjs.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

(AI) Right — fixed in 8e045a6: the line now reads WORKFLOW_VM=quickjs, matching the selector documented in the WORKFLOW_VM section below. Verified WORKFLOW_RUNTIME appears nowhere else in docs or code; my earlier reply on the quickjs-scope thread used the wrong name too, same correction applies there.

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

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

@github-actionsgithub-actionsBot mentioned this pull request Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

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

Labels

event-log-race-reproRun the event log race reproduction job

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@VaguelySerious@TooTallNate@pranaygp
, '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); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' [core] Pin correlation-id draw order to event-log order by VaguelySerious · Pull Request #3700 · vercel/workflow · GitHub
Skip to content

[core] Pin correlation-id draw order to event-log order - #3700

Merged
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws
Aug 21, 2026
Merged

[core] Pin correlation-id draw order to event-log order#3700
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Aug 20, 2026

Copy link
Copy Markdown
Member

(AI) Runtime refactor pinning correlation-id draw order to event-log order, the scheduler-side alternative to call-site addressing (#3179) for the residual CORRUPTED_EVENT_LOG class that survives #3554. Default on in this PR (WORKFLOW_LOG_ORDER_DRAWS=0 opts out). Based on main, which now carries #3696's blocked-branch scenario: that scenario reproduces the corruption on unfixed code at 6/6 (vercel), 6/6 (local), 3/6 (postgres) attempts, #3179's arm measured clean with call-site ids, and this PR's event-log-race-repro lane measures the draw-order fix with ids left positional.

Problem

Correlation ids are ordinals of one per-run draw sequence, so the id an entity gets depends on every draw before it. #3554 pinned delivery retirement to log order, but a delivery's continuation still needs a path-dependent number of microtask hops to reach its next draw, and the discipline let later deliveries resolve mid-cascade: a deferring delivery yielded exactly one macrotask, a delivery consumed after an earlier barrier deregistered saw an empty gate set and never yielded at all, and two deliveries waiting concurrently broke the tie by timer arrival. Draw order was therefore a function of arrival, and arrival depends on how much log a replay loaded. Two concurrent replays holding different-length dense prefixes could bind one ordinal to two entities (step_ vs wait_ in the five 2026-08-20 production failures), and every later replay dies on an unconsumable event.

What this changes

awaitEarlierDeliveries gains a flag-gated quiescence fixpoint that runs on every branch-deciding delivery, not only deferring ones. A delivery resolves to the workflow only when, over a full (promise-queue drain + macrotask) turn:

  1. no new correlation-id draws were made and no hydration is in flight (every earlier continuation has run as far as it can), and
  2. no lower-index armed barrier is still registered (no committed earlier delivery is still pending — the tie-break that timer order used to decide).

Unarmed buffered hook payloads keep their claim-driven handover (the gatesOn skip is untouched): a claim happens at a body position, which is already a function of the prefix, and gating steps on unclaimed payloads is what the pre-#3554 harness proved loses the ordering entirely once the idle safety net has to fire.

Draw order then becomes a pure function of the dense log: extending the log can only append draws, never renumber them, so concurrent writers' duplicate creates carry identical ids and dedup (#3381) absorbs them.

The context exposes a monotone mintCount as the quiescence progress metric; lightweight test contexts without it degrade to single-turn quiescence.

Tests

  • log-order-draws.test.ts: the blocked-branch production shape through runWorkflow, nothing hardcoded. Flag off (control): a dense prefix ending before a sibling's launch completion rebinds an ordinal from a finalize step to the sibling's watchdog wait. Flag on: every shared correlation id keeps its binding under pairwise extension AND across a sweep of every dense prefix of the log; replay is deterministic per prefix.
  • Full unit suite green in both modes (2213 passed each way), including the delivery-sensitive suites (storm-log-replay, delivery-barrier-coverage, step-delivery-ordering, hook-sleep-interaction, retained-vm-loop, hop-count). No fixture had to be pinned or rewritten: where arrival order was stable, log order agrees with it; the modes differ only where arrival order was load-dependent, which is the bug.
  • delivery-barrier-coverage.test.ts gains a termination pin for the turnstile's riskiest shape: a step delivery spinning above an armed wait that is itself parked behind an unclaimed buffered payload. Termination there rests on resolvesOnItsOwn excluding the spinner from hasParkedCommittedDelivery, which keeps the idle safety net's gate reachable so it can retire the payload and open the chain. The test replays that log in both flag states; a turnstile or idle-predicate change that breaks the argument deadlocks it instead of only hanging e2e lanes.

Costs and open questions

  1. Replay latency: one event-loop turn per branch-deciding delivery, more when continuations genuinely overlap. Review found the original setTimeout(0) turn cost ~1.1ms per delivery on sequential replays (Node clamps it); the turn is now setImmediate (~15-20µs), and a 100-step sequential replay measures 69.4ms flag-on vs 67.8ms flag-off (median of 5). The production storm-log replay is 30.9-31.3s in both modes, the hook e2e subset against a local prod server is timing-identical, and the unit suite total is unchanged. The benchmark lane's numbers on the current head are the remaining authoritative check.
  2. Engine scope: this applies to the default Node VM engine only. WORKFLOW_RUNTIME=quickjs has its own event feed and correlation-id sequence (quickjs-runtime.ts mints via __generateUlid and never touches the barrier registry), so the setting is inert there — and the repro lane measures the node engine.
  3. Live-side residual: this pins replay-driven scheduling. A resolution acted on in-process before its commit lands (and before a lower-slot event committed by another actor) is not reordered by this discipline; the orchestrator's iteration reload makes that window small, and the repro lane measures whether it matters in practice.
  4. Scheme migration: draw order changes for logs written under arrival order, so enabling/disabling with runs in flight is only safe under deployment pinning (true on Vercel via skew protection). Documented in runtime-tuning.mdx.

Relation to #3179

Complementary, not competing: this fixes the schedule (covers waits and every same-kind entity, closing the per-kind residual call-site ids leave), #3179 fixes the identity (robust even if the scheduler drifts again). Either alone should clear the repro; together they are defense in depth. The repro lanes on the three PRs are the comparison.

🤖 Generated with Claude Code

…OW_LOG_ORDER_DRAWS
A branch-deciding delivery now resolves to the workflow only after every
earlier-in-log delivery's continuation has quiesced (no new draws, no
hydration in flight, over a full macrotask turn) and no lower-slot armed
barrier is still undelivered. Draw order becomes a pure function of the
dense event log, stable under prefix extension, so concurrent replays
holding different-length prefixes mint compatible correlation ids and
their duplicate creates dedup instead of colliding (CORRUPTED_EVENT_LOG).
The single post-deferral macrotask yield this replaces was arrival-order:
a later delivery consumed after an earlier barrier deregistered saw an
empty gate set and resolved mid-cascade, and two deliveries quiescing
concurrently broke the tie by timer order. Buffered hook payloads keep
their claim-driven handover (the gatesOn skip is unchanged): a claim is
body-position determined, already a function of the prefix.
Offline regression: the blocked-branch production shape replayed across
every dense prefix, flag off (control, rebinds an ordinal step->wait) and
flag on (every shared id keeps its binding). Full unit suite green in
both modes. Default on for the repro A/B, mirroring the call-site-ids arm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-botBot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8e045a6

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

This PR includes changesets to release 16 packages
NameType
workflowMinor
@workflow/coreMinor
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@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 20, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-nextjs-workflow-webpackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-astro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-express-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-fastify-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-hono-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nestjs-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nitro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nuxt-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-python-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-sveltekit-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-tanstack-start-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-vite-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-docsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-swc-playgroundReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-tarballsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-webReadyReadyPreview, v0Aug 21, 2026 6:34pm

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (241 failed)

astro-node (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

astro-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

example-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

example-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

express-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

express-quickjs (12 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify-node (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

fastify-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

hono-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

hono-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nest-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nest-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-turbopack-node (5 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nextjs-turbopack-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nitro-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nitro-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

sveltekit-node (5 failed):

  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

sveltekit-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

tanstack-start-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

tanstack-start-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-node (8 failed):

  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

⚠️ Flaky E2E Tests (passed on retry)

These tests failed at least once and passed on a retry. A recurring entry here is a real race worth investigating.

24 flaky tests
  • abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook (sveltekit)
  • abortParallelWorkflow: abort cancels all parallel steps (nextjs-webpack)
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously (sveltekit)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (nitro)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (tanstack-start)
  • hookCleanupTestWorkflow - hook token reuse after workflow completion (sveltekit)
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable (sveltekit)
  • Promise.all of disjoint-key writes: every key lands (example)
  • Promise.all of disjoint-key writes: every key lands (nest)
  • Promise.all of disjoint-key writes: every key lands (nuxt)
  • Promise.all of disjoint-key writes: every key lands (tanstack-start)
  • setAttributesInsideStepWorkflow: step-body calls append attributed native events (nextjs-turbopack)
  • setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly (vite)
  • sleepWinsRaceWorkflow (vite)
  • start: initial attributes are seeded on run creation (example)
  • start: initial attributes are seeded on run creation (nextjs-turbopack)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (express)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (fastify)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (nitro)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (tanstack-start)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (express)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (fastify)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-turbopack)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-webpack)

🛠 Infra Events (absorbed by the harness)

Platform anomalies the e2e harness detected and worked around (e.g. a run the queue never picked up, replaced by a fresh run). Clustered timestamps indicate a backend blip; a steady drip indicates a platform issue worth escalating.

  • cold-start-warmup · suite warmup (tanstack-start) · at 18:35:21Z · abandoned wrun_01M0JSNG5YY2EJ38F5CWJ1C0R0
  • run-pickup-stall · hookCleanupTestWorkflow - hook token reuse after workflow completion (nextjs-webpack) · at 18:41:40Z · abandoned wrun_01M0JT1EKJHZM46BTW6J4K101P

E2E Test Summary

Summary
PassedFailedSkippedTotal
❌ ▲ Vercel Production33372417424320
✅ 💻 Local Development392205584480
✅ 📦 Local Production392205584480
✅ 🐘 Local Postgres392205584480
✅ 🪟 Windows32000320
✅ 🌐 Cross-language Conformance90132141
✅ vercel-multi-region270027
Total15459241254818248
Details by Category

❌ ▲ Vercel Production

AppPassedFailedSkipped
❌ astro-node1211128
❌ astro-quickjs123928
❌ example-node123928
❌ example-quickjs1211128
❌ express-node123928
❌ express-quickjs1201228
❌ fastify-node1221028
❌ fastify-quickjs1221028
❌ hono-node125728
❌ hono-quickjs123928
❌ nest-node123928
❌ nest-quickjs1211128
❌ nextjs-turbopack-node15253
❌ nextjs-turbopack-quickjs14893
❌ nextjs-webpack-node14893
❌ nextjs-webpack-quickjs146113
❌ nitro-node125728
❌ nitro-quickjs1211128
❌ nuxt-node124828
❌ nuxt-quickjs1211128
✅ python-node80152
❌ sveltekit-node14659
❌ sveltekit-quickjs140119
❌ tanstack-start-node124828
❌ tanstack-start-quickjs1221028
❌ vite-node124828
❌ vite-quickjs1211128

✅ 💻 Local Development

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 📦 Local Production

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🐘 Local Postgres

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🪟 Windows

AppPassedFailedSkipped
✅ nextjs-turbopack-node16000
✅ nextjs-turbopack-quickjs16000

✅ 🌐 Cross-language Conformance

AppPassedFailedSkipped
✅ python90132

✅ vercel-multi-region

AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

The benchmark run for 8e045a6 failed. See the run logs for details.

No benchmark results were produced.

ℹ️ Metric definitions & methodology

Metrics —

All timestamps are deployment-side; runs are triggered in-deployment, so the CI runner and api.vercel.com sit outside every measured window. TTFS = start() → first step body (includes dispatch + any cold start); Fan-out TTFS/TTLS = first/last step completion of one Promise.all from the same anchor (the gap is the runtime’s fan-out spread); STSO/WO between step bodies; CRTT inside the workflow (excludes the api.vercel.com read path).

Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor.

@VaguelySeriousVaguelySerious added the event-log-race-repro Run the event log race reproduction job label Aug 20, 2026
@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

🟠 world-sim scenario book — 1 fail of 41 total

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.txt

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Event Log Race Repro

  • vercel clean, 20 runs
  • local clean, 20 runs
  • postgres clean, 20 runs

Run History

RunLaneTotalCompleteCorruptStuckOther
08-20 22:40vercel2020000
local2020000
postgres2020000
08-21 16:46vercel
local
postgres
08-21 16:56vercel2020000
local2020000
postgres2020000
08-21 17:56vercel2020000
local2020000
postgres2020000
08-21 18:40vercel2020000
local2020000
postgres2020000
Config

20 runs / step-storm 6, hook-storm 6, blocked-branch 6, hook-sleep 2 / c8 / 6x8 / watchdog 2500ms / step 2200±250ms / stagger 400ms / burst 4000+1200ms / poke 750ms / poke max 64 / timeout 240000ms

A step delivery's log-order turnstile waits on lower-index ARMED barriers,
and an armed wait can be parked behind an unclaimed buffered hook payload
that only the idle-gated safety net can retire. Termination there rests on
the spinning delivery not counting as a parked committed delivery
(resolvesOnItsOwn excludes it), keeping the net's gate reachable. Pin that
shape with a replay in both flag states so a turnstile or idle-predicate
change that deadlocks it fails fast instead of only hanging e2e lanes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@vercelvercelBot 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.

Additional Suggestion:

The generated e2e diagnostics sidecar glob (e2e-diagnostics-*.json) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.

Fix on Vercel

…son`) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.
This commit fixes the issue reported at .gitignore:53
## Status: partially fixed, gitignore rule still needed
The stray artifact `e2e-diagnostics-express-local.json` from this PR was **removed** by the follow-up commit `fd6e18a` ("Remove stray local e2e diagnostics artifact"). That part of the original suggestion is now done.
However, the **root cause remains unaddressed**: there is still no `.gitignore` rule for the sidecar pattern, so the harness will re-stage these files on the next e2e run.
### Evidence
- `git ls-files | grep e2e-diagnostics` no longer lists `e2e-diagnostics-express-local.json` (removed), but **still** lists a pre-existing sidecar `e2e-diagnostics-nextjs-turbopack-vercel.json` — proof this is a recurring accidental-commit pattern.
- `git check-ignore e2e-diagnostics-express-local.json` returned exit `1` (no matching pattern) before this change.
- `writeDiagnosticsSidecar()` in `packages/core/e2e/utils.ts` writes `e2e-diagnostics-<APP_NAME>-<backend>.json` to `process.cwd()` during e2e runs — clearly per-run output, not source.
### Fix
Add an ignore rule to `.gitignore` (file-removal is already handled by `fd6e18a`):
```
# Per-run e2e diagnostics sidecars written to the repo root by the harness
# (writeDiagnosticsSidecar in packages/core/e2e/utils.ts)
e2e-diagnostics-*.json
```
After the change, `git check-ignore e2e-diagnostics-express-local.json` matches (exit `0`), so future runs won't re-stage the sidecar. The already-tracked `e2e-diagnostics-nextjs-turbopack-vercel.json` is left in place (out of scope), though the new rule covers that pattern for any future untracked instances.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
TooTallNate
TooTallNate previously requested changes Aug 21, 2026

@TooTallNateTooTallNate 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.

AI: Request changes. The new runtime-tuning documentation points users to the wrong VM selector. I found no additional blocking code issues.

- Experimental. Pins correlation-ID draw order to event-log order: a branch-deciding delivery (a step result, hook payload, or wait completion) resolves to the workflow only after every earlier-in-log delivery's continuation has fully quiesced, and never ahead of a lower-slot delivery that is committed to happening.
- Without it, a delivery that resolves while an earlier delivery's continuation is still a few microtask hops from its next step/hook/wait call can overtake it on the run's shared correlation-ID sequence. Draw order — and therefore correlation IDs — then depends on how much of the event log a replay had loaded, and two concurrent replays holding different-length prefixes can bind one ID to two different entities, failing the run with `CORRUPTED_EVENT_LOG`.
- Costs one event-loop turn (roughly 15-20 microseconds via `setImmediate`) per branch-deciding delivery during replay, more when continuations genuinely overlap. Measured on a 100-step sequential replay: about 2ms added end to end.
- Only applies to the default Node.js VM engine. `WORKFLOW_RUNTIME=quickjs` has its own event feed and correlation-ID sequence and is unaffected by this setting.

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.

AI: The engine selector is WORKFLOW_VM, as documented below, not WORKFLOW_RUNTIME. As written, users following this guidance will remain on the Node VM while believing they selected QuickJS. Please change this to WORKFLOW_VM=quickjs.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

(AI) Right — fixed in 8e045a6: the line now reads WORKFLOW_VM=quickjs, matching the selector documented in the WORKFLOW_VM section below. Verified WORKFLOW_RUNTIME appears nowhere else in docs or code; my earlier reply on the quickjs-scope thread used the wrong name too, same correction applies there.

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

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

@github-actionsgithub-actionsBot mentioned this pull request Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

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

Labels

event-log-race-reproRun the event log race reproduction job

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@VaguelySerious@TooTallNate@pranaygp
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); [core] Pin correlation-id draw order to event-log order by VaguelySerious · Pull Request #3700 · vercel/workflow · GitHub
Skip to content

[core] Pin correlation-id draw order to event-log order - #3700

Merged
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws
Aug 21, 2026
Merged

[core] Pin correlation-id draw order to event-log order#3700
VaguelySerious merged 6 commits into
mainfrom
peter/log-order-draws

Conversation

@VaguelySerious

@VaguelySeriousVaguelySerious commented Aug 20, 2026

Copy link
Copy Markdown
Member

(AI) Runtime refactor pinning correlation-id draw order to event-log order, the scheduler-side alternative to call-site addressing (#3179) for the residual CORRUPTED_EVENT_LOG class that survives #3554. Default on in this PR (WORKFLOW_LOG_ORDER_DRAWS=0 opts out). Based on main, which now carries #3696's blocked-branch scenario: that scenario reproduces the corruption on unfixed code at 6/6 (vercel), 6/6 (local), 3/6 (postgres) attempts, #3179's arm measured clean with call-site ids, and this PR's event-log-race-repro lane measures the draw-order fix with ids left positional.

Problem

Correlation ids are ordinals of one per-run draw sequence, so the id an entity gets depends on every draw before it. #3554 pinned delivery retirement to log order, but a delivery's continuation still needs a path-dependent number of microtask hops to reach its next draw, and the discipline let later deliveries resolve mid-cascade: a deferring delivery yielded exactly one macrotask, a delivery consumed after an earlier barrier deregistered saw an empty gate set and never yielded at all, and two deliveries waiting concurrently broke the tie by timer arrival. Draw order was therefore a function of arrival, and arrival depends on how much log a replay loaded. Two concurrent replays holding different-length dense prefixes could bind one ordinal to two entities (step_ vs wait_ in the five 2026-08-20 production failures), and every later replay dies on an unconsumable event.

What this changes

awaitEarlierDeliveries gains a flag-gated quiescence fixpoint that runs on every branch-deciding delivery, not only deferring ones. A delivery resolves to the workflow only when, over a full (promise-queue drain + macrotask) turn:

  1. no new correlation-id draws were made and no hydration is in flight (every earlier continuation has run as far as it can), and
  2. no lower-index armed barrier is still registered (no committed earlier delivery is still pending — the tie-break that timer order used to decide).

Unarmed buffered hook payloads keep their claim-driven handover (the gatesOn skip is untouched): a claim happens at a body position, which is already a function of the prefix, and gating steps on unclaimed payloads is what the pre-#3554 harness proved loses the ordering entirely once the idle safety net has to fire.

Draw order then becomes a pure function of the dense log: extending the log can only append draws, never renumber them, so concurrent writers' duplicate creates carry identical ids and dedup (#3381) absorbs them.

The context exposes a monotone mintCount as the quiescence progress metric; lightweight test contexts without it degrade to single-turn quiescence.

Tests

  • log-order-draws.test.ts: the blocked-branch production shape through runWorkflow, nothing hardcoded. Flag off (control): a dense prefix ending before a sibling's launch completion rebinds an ordinal from a finalize step to the sibling's watchdog wait. Flag on: every shared correlation id keeps its binding under pairwise extension AND across a sweep of every dense prefix of the log; replay is deterministic per prefix.
  • Full unit suite green in both modes (2213 passed each way), including the delivery-sensitive suites (storm-log-replay, delivery-barrier-coverage, step-delivery-ordering, hook-sleep-interaction, retained-vm-loop, hop-count). No fixture had to be pinned or rewritten: where arrival order was stable, log order agrees with it; the modes differ only where arrival order was load-dependent, which is the bug.
  • delivery-barrier-coverage.test.ts gains a termination pin for the turnstile's riskiest shape: a step delivery spinning above an armed wait that is itself parked behind an unclaimed buffered payload. Termination there rests on resolvesOnItsOwn excluding the spinner from hasParkedCommittedDelivery, which keeps the idle safety net's gate reachable so it can retire the payload and open the chain. The test replays that log in both flag states; a turnstile or idle-predicate change that breaks the argument deadlocks it instead of only hanging e2e lanes.

Costs and open questions

  1. Replay latency: one event-loop turn per branch-deciding delivery, more when continuations genuinely overlap. Review found the original setTimeout(0) turn cost ~1.1ms per delivery on sequential replays (Node clamps it); the turn is now setImmediate (~15-20µs), and a 100-step sequential replay measures 69.4ms flag-on vs 67.8ms flag-off (median of 5). The production storm-log replay is 30.9-31.3s in both modes, the hook e2e subset against a local prod server is timing-identical, and the unit suite total is unchanged. The benchmark lane's numbers on the current head are the remaining authoritative check.
  2. Engine scope: this applies to the default Node VM engine only. WORKFLOW_RUNTIME=quickjs has its own event feed and correlation-id sequence (quickjs-runtime.ts mints via __generateUlid and never touches the barrier registry), so the setting is inert there — and the repro lane measures the node engine.
  3. Live-side residual: this pins replay-driven scheduling. A resolution acted on in-process before its commit lands (and before a lower-slot event committed by another actor) is not reordered by this discipline; the orchestrator's iteration reload makes that window small, and the repro lane measures whether it matters in practice.
  4. Scheme migration: draw order changes for logs written under arrival order, so enabling/disabling with runs in flight is only safe under deployment pinning (true on Vercel via skew protection). Documented in runtime-tuning.mdx.

Relation to #3179

Complementary, not competing: this fixes the schedule (covers waits and every same-kind entity, closing the per-kind residual call-site ids leave), #3179 fixes the identity (robust even if the scheduler drifts again). Either alone should clear the repro; together they are defense in depth. The repro lanes on the three PRs are the comparison.

🤖 Generated with Claude Code

…OW_LOG_ORDER_DRAWS
A branch-deciding delivery now resolves to the workflow only after every
earlier-in-log delivery's continuation has quiesced (no new draws, no
hydration in flight, over a full macrotask turn) and no lower-slot armed
barrier is still undelivered. Draw order becomes a pure function of the
dense event log, stable under prefix extension, so concurrent replays
holding different-length prefixes mint compatible correlation ids and
their duplicate creates dedup instead of colliding (CORRUPTED_EVENT_LOG).
The single post-deferral macrotask yield this replaces was arrival-order:
a later delivery consumed after an earlier barrier deregistered saw an
empty gate set and resolved mid-cascade, and two deliveries quiescing
concurrently broke the tie by timer order. Buffered hook payloads keep
their claim-driven handover (the gatesOn skip is unchanged): a claim is
body-position determined, already a function of the prefix.
Offline regression: the blocked-branch production shape replayed across
every dense prefix, flag off (control, rebinds an ordinal step->wait) and
flag on (every shared id keeps its binding). Full unit suite green in
both modes. Default on for the repro A/B, mirroring the call-site-ids arm.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-botBot commented Aug 20, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 8e045a6

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

This PR includes changesets to release 16 packages
NameType
workflowMinor
@workflow/coreMinor
@workflow/world-testingPatch
@workflow/buildersPatch
@workflow/cliPatch
@workflow/nextPatch
@workflow/nitroPatch
@workflow/vitestPatch
@workflow/web-sharedPatch
@workflow/webPatch
@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 20, 2026

Copy link
Copy Markdown
Contributor

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

ProjectDeploymentActionsUpdated (UTC)
example-nextjs-workflow-turbopackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-nextjs-workflow-webpackReadyReadyPreview, v0Aug 21, 2026 6:34pm
example-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-astro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-express-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-fastify-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-hono-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nestjs-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nitro-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-nuxt-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-python-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-sveltekit-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-tanstack-start-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workbench-vite-workflowReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-docsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-swc-playgroundReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-tarballsReadyReadyPreview, v0Aug 21, 2026 6:34pm
workflow-webReadyReadyPreview, v0Aug 21, 2026 6:34pm

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (241 failed)

astro-node (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

astro-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

example-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

example-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

express-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

express-quickjs (12 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

fastify-node (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands

fastify-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

hono-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

hono-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nest-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nest-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-turbopack-node (5 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nextjs-turbopack-quickjs (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-node (9 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nextjs-webpack-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

nitro-node (7 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

nitro-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

nuxt-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

sveltekit-node (5 failed):

  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting

sveltekit-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

tanstack-start-node (8 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run

tanstack-start-quickjs (10 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-node (8 failed):

  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

vite-quickjs (11 failed):

  • hookCleanupTestWorkflow - hook token reuse after workflow completion | wrun_41M0JSVY020GW8FTGZZ6B94BYG | 🔍 observability
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously | wrun_41M0JSV2C20GSPE1D474AQ5VTP | 🔍 observability
  • hookGetConflictWorkflow - hook.getConflict() resolves with the conflicting run when token is already registered | wrun_41M0JSWV530GS39VEYYXJ460TA | 🔍 observability
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable | wrun_41M0JSXSEY0GYS1X4SBW0VFHK6 | 🔍 observability
  • setAttributes start: initial attributes are seeded on run creation
  • setAttributes start: reserved-prefix initial attributes are seeded with allowReservedAttributes
  • setAttributes setAttributesInsideStepWorkflow: step-body calls append attributed native events
  • setAttributes fire-and-forget: void setAttributes lands without awaiting
  • setAttributes Promise.all of disjoint-key writes: every key lands
  • setAttributes workflow throws after awaited setAttributes: attribute still persists on the failed run
  • setAttributes validation DX: invalid writes throw catchable FatalErrors naming rule and limit

⚠️ Flaky E2E Tests (passed on retry)

These tests failed at least once and passed on a retry. A recurring entry here is a real race worth investigating.

24 flaky tests
  • abortHookOrderingWorkflow [hook-first-abort-first]: hook.then → addEventListener → abort() → resumeHook (sveltekit)
  • abortParallelWorkflow: abort cancels all parallel steps (nextjs-webpack)
  • concurrent hook token conflict - two workflows cannot use the same hook token simultaneously (sveltekit)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (nitro)
  • deploymentId: 'latest' is a no-op in non-Vercel worlds (tanstack-start)
  • hookCleanupTestWorkflow - hook token reuse after workflow completion (sveltekit)
  • hookMinRetentionWorkflow - terminal Hook cannot resume and its token stays unavailable (sveltekit)
  • Promise.all of disjoint-key writes: every key lands (example)
  • Promise.all of disjoint-key writes: every key lands (nest)
  • Promise.all of disjoint-key writes: every key lands (nuxt)
  • Promise.all of disjoint-key writes: every key lands (tanstack-start)
  • setAttributesInsideStepWorkflow: step-body calls append attributed native events (nextjs-turbopack)
  • setAttributesWorkflow: workflow-body calls append native attr_set events and merge correctly (vite)
  • sleepWinsRaceWorkflow (vite)
  • start: initial attributes are seeded on run creation (example)
  • start: initial attributes are seeded on run creation (nextjs-turbopack)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (express)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (fastify)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (nitro)
  • validation DX: invalid writes throw catchable FatalErrors naming rule and limit (tanstack-start)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (express)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (fastify)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-turbopack)
  • workflow throws after awaited setAttributes: attribute still persists on the failed run (nextjs-webpack)

🛠 Infra Events (absorbed by the harness)

Platform anomalies the e2e harness detected and worked around (e.g. a run the queue never picked up, replaced by a fresh run). Clustered timestamps indicate a backend blip; a steady drip indicates a platform issue worth escalating.

  • cold-start-warmup · suite warmup (tanstack-start) · at 18:35:21Z · abandoned wrun_01M0JSNG5YY2EJ38F5CWJ1C0R0
  • run-pickup-stall · hookCleanupTestWorkflow - hook token reuse after workflow completion (nextjs-webpack) · at 18:41:40Z · abandoned wrun_01M0JT1EKJHZM46BTW6J4K101P

E2E Test Summary

Summary
PassedFailedSkippedTotal
❌ ▲ Vercel Production33372417424320
✅ 💻 Local Development392205584480
✅ 📦 Local Production392205584480
✅ 🐘 Local Postgres392205584480
✅ 🪟 Windows32000320
✅ 🌐 Cross-language Conformance90132141
✅ vercel-multi-region270027
Total15459241254818248
Details by Category

❌ ▲ Vercel Production

AppPassedFailedSkipped
❌ astro-node1211128
❌ astro-quickjs123928
❌ example-node123928
❌ example-quickjs1211128
❌ express-node123928
❌ express-quickjs1201228
❌ fastify-node1221028
❌ fastify-quickjs1221028
❌ hono-node125728
❌ hono-quickjs123928
❌ nest-node123928
❌ nest-quickjs1211128
❌ nextjs-turbopack-node15253
❌ nextjs-turbopack-quickjs14893
❌ nextjs-webpack-node14893
❌ nextjs-webpack-quickjs146113
❌ nitro-node125728
❌ nitro-quickjs1211128
❌ nuxt-node124828
❌ nuxt-quickjs1211128
✅ python-node80152
❌ sveltekit-node14659
❌ sveltekit-quickjs140119
❌ tanstack-start-node124828
❌ tanstack-start-quickjs1221028
❌ vite-node124828
❌ vite-quickjs1211128

✅ 💻 Local Development

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 📦 Local Production

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🐘 Local Postgres

AppPassedFailedSkipped
✅ astro-stable-node134026
✅ astro-stable-quickjs134026
✅ express-stable-node134026
✅ express-stable-quickjs134026
✅ fastify-stable-node134026
✅ fastify-stable-quickjs134026
✅ hono-stable-node134026
✅ hono-stable-quickjs134026
✅ nest-stable-node134026
✅ nest-stable-quickjs134026
✅ nextjs-turbopack-canary-node141019
✅ nextjs-turbopack-canary-quickjs141019
✅ nextjs-turbopack-stable-node16000
✅ nextjs-turbopack-stable-quickjs16000
✅ nextjs-webpack-canary-node141019
✅ nextjs-webpack-canary-quickjs141019
✅ nextjs-webpack-stable-node16000
✅ nextjs-webpack-stable-quickjs16000
✅ nitro-stable-node134026
✅ nitro-stable-quickjs134026
✅ nuxt-stable-node134026
✅ nuxt-stable-quickjs134026
✅ sveltekit-stable-node15307
✅ sveltekit-stable-quickjs15307
✅ tanstack-start-node134026
✅ tanstack-start-quickjs134026
✅ vite-stable-node134026
✅ vite-stable-quickjs134026

✅ 🪟 Windows

AppPassedFailedSkipped
✅ nextjs-turbopack-node16000
✅ nextjs-turbopack-quickjs16000

✅ 🌐 Cross-language Conformance

AppPassedFailedSkipped
✅ python90132

✅ vercel-multi-region

AppPassedFailedSkipped
✅ nextjs-turbopack2700

📋 View full workflow run

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

The benchmark run for 8e045a6 failed. See the run logs for details.

No benchmark results were produced.

ℹ️ Metric definitions & methodology

Metrics —

All timestamps are deployment-side; runs are triggered in-deployment, so the CI runner and api.vercel.com sit outside every measured window. TTFS = start() → first step body (includes dispatch + any cold start); Fan-out TTFS/TTLS = first/last step completion of one Promise.all from the same anchor (the gap is the runtime’s fan-out spread); STSO/WO between step bodies; CRTT inside the workflow (excludes the api.vercel.com read path).

Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor.

@VaguelySeriousVaguelySerious added the event-log-race-repro Run the event log race reproduction job label Aug 20, 2026
@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

🟠 world-sim scenario book — 1 fail of 41 total

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.txt

@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Event Log Race Repro

  • vercel clean, 20 runs
  • local clean, 20 runs
  • postgres clean, 20 runs

Run History

RunLaneTotalCompleteCorruptStuckOther
08-20 22:40vercel2020000
local2020000
postgres2020000
08-21 16:46vercel
local
postgres
08-21 16:56vercel2020000
local2020000
postgres2020000
08-21 17:56vercel2020000
local2020000
postgres2020000
08-21 18:40vercel2020000
local2020000
postgres2020000
Config

20 runs / step-storm 6, hook-storm 6, blocked-branch 6, hook-sleep 2 / c8 / 6x8 / watchdog 2500ms / step 2200±250ms / stagger 400ms / burst 4000+1200ms / poke 750ms / poke max 64 / timeout 240000ms

A step delivery's log-order turnstile waits on lower-index ARMED barriers,
and an armed wait can be parked behind an unclaimed buffered hook payload
that only the idle-gated safety net can retire. Termination there rests on
the spinning delivery not counting as a parked committed delivery
(resolvesOnItsOwn excludes it), keeping the net's gate reachable. Pin that
shape with a replay in both flag states so a turnstile or idle-predicate
change that deadlocks it fails fast instead of only hanging e2e lanes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@vercelvercelBot 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.

Additional Suggestion:

The generated e2e diagnostics sidecar glob (e2e-diagnostics-*.json) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.

Fix on Vercel

…son`) is not gitignored, so per-run harness output written to the repo root keeps getting accidentally committed.
This commit fixes the issue reported at .gitignore:53
## Status: partially fixed, gitignore rule still needed
The stray artifact `e2e-diagnostics-express-local.json` from this PR was **removed** by the follow-up commit `fd6e18a` ("Remove stray local e2e diagnostics artifact"). That part of the original suggestion is now done.
However, the **root cause remains unaddressed**: there is still no `.gitignore` rule for the sidecar pattern, so the harness will re-stage these files on the next e2e run.
### Evidence
- `git ls-files | grep e2e-diagnostics` no longer lists `e2e-diagnostics-express-local.json` (removed), but **still** lists a pre-existing sidecar `e2e-diagnostics-nextjs-turbopack-vercel.json` — proof this is a recurring accidental-commit pattern.
- `git check-ignore e2e-diagnostics-express-local.json` returned exit `1` (no matching pattern) before this change.
- `writeDiagnosticsSidecar()` in `packages/core/e2e/utils.ts` writes `e2e-diagnostics-<APP_NAME>-<backend>.json` to `process.cwd()` during e2e runs — clearly per-run output, not source.
### Fix
Add an ignore rule to `.gitignore` (file-removal is already handled by `fd6e18a`):
```
# Per-run e2e diagnostics sidecars written to the repo root by the harness
# (writeDiagnosticsSidecar in packages/core/e2e/utils.ts)
e2e-diagnostics-*.json
```
After the change, `git check-ignore e2e-diagnostics-express-local.json` matches (exit `0`), so future runs won't re-stage the sidecar. The already-tracked `e2e-diagnostics-nextjs-turbopack-vercel.json` is left in place (out of scope), though the new rule covers that pattern for any future untracked instances.
Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com>
Co-authored-by: VaguelySerious <mittgfu@gmail.com>
TooTallNate
TooTallNate previously requested changes Aug 21, 2026

@TooTallNateTooTallNate 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.

AI: Request changes. The new runtime-tuning documentation points users to the wrong VM selector. I found no additional blocking code issues.

- Experimental. Pins correlation-ID draw order to event-log order: a branch-deciding delivery (a step result, hook payload, or wait completion) resolves to the workflow only after every earlier-in-log delivery's continuation has fully quiesced, and never ahead of a lower-slot delivery that is committed to happening.
- Without it, a delivery that resolves while an earlier delivery's continuation is still a few microtask hops from its next step/hook/wait call can overtake it on the run's shared correlation-ID sequence. Draw order — and therefore correlation IDs — then depends on how much of the event log a replay had loaded, and two concurrent replays holding different-length prefixes can bind one ID to two different entities, failing the run with `CORRUPTED_EVENT_LOG`.
- Costs one event-loop turn (roughly 15-20 microseconds via `setImmediate`) per branch-deciding delivery during replay, more when continuations genuinely overlap. Measured on a 100-step sequential replay: about 2ms added end to end.
- Only applies to the default Node.js VM engine. `WORKFLOW_RUNTIME=quickjs` has its own event feed and correlation-ID sequence and is unaffected by this setting.

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.

AI: The engine selector is WORKFLOW_VM, as documented below, not WORKFLOW_RUNTIME. As written, users following this guidance will remain on the Node VM while believing they selected QuickJS. Please change this to WORKFLOW_VM=quickjs.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

(AI) Right — fixed in 8e045a6: the line now reads WORKFLOW_VM=quickjs, matching the selector documented in the WORKFLOW_VM section below. Verified WORKFLOW_RUNTIME appears nowhere else in docs or code; my earlier reply on the quickjs-scope thread used the wrong name too, same correction applies there.

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

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

@github-actionsgithub-actionsBot mentioned this pull request Aug 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Backport to stable failed for 9b1b8c7 due to a workflow error (backport job run).

This is usually an infrastructure problem (e.g. the configured AI model could not be found, an AI Gateway error, or an opencode crash) rather than a merge conflict. Check the job logs linked above for details.

Once the underlying issue is fixed, re-run the Backport to stable workflow manually via workflow_dispatch and paste this commit SHA into the ref input:

9b1b8c711104fd507327aafc8cb965738f315e29

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

Labels

event-log-race-reproRun the event log race reproduction job

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@VaguelySerious@TooTallNate@pranaygp