Skip to content

[repro] Bisect: known-good base + #3198 only - #3238

Closed
pranaygp wants to merge 1 commit into
mainfrom
pgp/bisect-3198-only
Closed

[repro] Bisect: known-good base + #3198 only#3238
pranaygp wants to merge 1 commit into
mainfrom
pgp/bisect-3198-only

Conversation

@pranaygp

Copy link
Copy Markdown
Contributor

Storm bisect for the hook-storm regression that entered main in e8934ad..b12f248 (replicated on two independent branches: 145/600→596/600 and 95/600→600/600 across the same merge). This branch = known-good e8934ad + cherry-pick of #3198 (barrier idle/parked-delivery change) ONLY. Companion: base + #3190 only. Known-good base measured hook-storm 145/600 (run 30494191064). Not for merge.

🤖 Generated with Claude Code

…hind its deferral (#3198)

(cherry picked from commit b92c23c)
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Copilot AI review requested due to automatic review settings July 30, 2026 22:44
@pranaygp
pranaygp requested a review from ijjk as a code owner July 30, 2026 22:44
@pranaygp pranaygp added the event-log-race-repro Run the event log race reproduction job label Jul 30, 2026
@pranaygp
pranaygp requested a review from a team as a code owner July 30, 2026 22:44
@changeset-bot

changeset-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: d4f34fb

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

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

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

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

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

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

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

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

Some tests failed

❌ Failed E2E Tests

▲ Vercel Production (3 failed)

hono (1 failed):

  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KYTKRR9M0GZHDK976R2FBTMQ | 🔍 observability

nextjs-turbopack (1 failed):

  • closureVariableWorkflow - nested step functions with closure variables | wrun_41KYTKPY7C0GX22RDK5NQJKM5B | 🔍 observability

sveltekit (1 failed):

  • customSerializationWorkflow - custom class serialization with WORKFLOW_SERIALIZE/WORKFLOW_DESERIALIZE | wrun_41KYTKRXCP0GX4NVD3D7CX8Q5G | 🔍 observability

📋 Other (2 failed)

e2e-vercel-prod-nest (1 failed):

  • thisSerializationWorkflow - step function invoked with .call() and .apply() | wrun_41KYTKRR9M0GZHDK976R2FBTMQ

e2e-vercel-prod-tanstack-start (1 failed):

  • hookWithSleepWorkflow - hook payloads delivered correctly with concurrent sleep | wrun_41KYTKTA480GK9MGAMWW1X860V

E2E Test Summary

Summary
Passed Failed Skipped Total
❌ ▲ Vercel Production 1452 3 239 1694
✅ 💻 Local Development 1621 0 227 1848
✅ 📦 Local Production 1621 0 227 1848
✅ 🐘 Local Postgres 1621 0 227 1848
✅ 🪟 Windows 154 0 0 154
❌ 📋 Other 1018 2 212 1232
✅ vercel-multi-region 27 0 0 27
Total 7514 5 1132 8651
Details by Category

❌ ▲ Vercel Production

App Passed Failed Skipped
✅ astro 126 0 28
✅ example 126 0 28
✅ express 126 0 28
✅ fastify 126 0 28
❌ hono 125 1 28
❌ nextjs-turbopack 150 1 3
✅ nextjs-webpack 151 0 3
✅ nitro 126 0 28
✅ nuxt 126 0 28
❌ sveltekit 144 1 9
✅ vite 126 0 28

✅ 💻 Local Development

App Passed Failed Skipped
✅ astro-stable 128 0 26
✅ express-stable 128 0 26
✅ fastify-stable 128 0 26
✅ hono-stable 128 0 26
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-turbopack-stable 154 0 0
✅ nextjs-webpack-canary 135 0 19
✅ nextjs-webpack-stable 154 0 0
✅ nitro-stable 128 0 26
✅ nuxt-stable 128 0 26
✅ sveltekit-stable 147 0 7
✅ vite-stable 128 0 26

✅ 📦 Local Production

App Passed Failed Skipped
✅ astro-stable 128 0 26
✅ express-stable 128 0 26
✅ fastify-stable 128 0 26
✅ hono-stable 128 0 26
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-turbopack-stable 154 0 0
✅ nextjs-webpack-canary 135 0 19
✅ nextjs-webpack-stable 154 0 0
✅ nitro-stable 128 0 26
✅ nuxt-stable 128 0 26
✅ sveltekit-stable 147 0 7
✅ vite-stable 128 0 26

✅ 🐘 Local Postgres

App Passed Failed Skipped
✅ astro-stable 128 0 26
✅ express-stable 128 0 26
✅ fastify-stable 128 0 26
✅ hono-stable 128 0 26
✅ nextjs-turbopack-canary 135 0 19
✅ nextjs-turbopack-stable 154 0 0
✅ nextjs-webpack-canary 135 0 19
✅ nextjs-webpack-stable 154 0 0
✅ nitro-stable 128 0 26
✅ nuxt-stable 128 0 26
✅ sveltekit-stable 147 0 7
✅ vite-stable 128 0 26

✅ 🪟 Windows

App Passed Failed Skipped
✅ nextjs-turbopack 154 0 0

❌ 📋 Other

App Passed Failed Skipped
✅ e2e-local-dev-nest-stable 128 0 26
✅ e2e-local-dev-tanstack-start- 128 0 26
✅ e2e-local-postgres-nest-stable 128 0 26
✅ e2e-local-postgres-tanstack-start- 128 0 26
✅ e2e-local-prod-nest-stable 128 0 26
✅ e2e-local-prod-tanstack-start- 128 0 26
❌ e2e-vercel-prod-nest 125 1 28
❌ e2e-vercel-prod-tanstack-start 125 1 28

✅ vercel-multi-region

App Passed Failed Skipped
✅ nextjs-turbopack 27 0 0

📋 View full workflow run

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

commit d4f34fb · Thu, 30 Jul 2026 23:06:05 GMT · run logs

Backend: vercel · app: nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 235 (-73%) 💚 920 🔴 (-8.9%) 1282 🔴 (+13%) 1317 🔴 (-16%) 💚 30
TTFS stream 226 (-75%) 💚 707 🔴 (-28%) 💚 1301 🔴 (+31%) 🔻 1349 🔴 (+32%) 🔻 30
TTFS hook + stream 389 (-66%) 💚 1146 🔴 (-8.6%) 1494 🔴 (+15%) 1585 🔴 (-8.0%) 30
STSO 1020 steps (inline) 173 (+6.8%) 481 (+2.8%) 547 (+4.8%) 715 (+8.2%) 1019
WO 1020 steps 413902 (+4.2%) 413902 (+4.2%) 413902 (+4.2%) 413902 (+4.2%) 1
SL stream latency 120 (+43%) 🔻 308 🔴 (+89%) 🔻 536 🔴 (+119%) 🔻 978 🔴 (+160%) 🔻 30
SO stream overhead (text) 168 (+51%) 🔻 326 🔴 (+79%) 🔻 705 🔴 (+107%) 🔻 1360 🔴 (+132%) 🔻 30
SO stream overhead (structured) 156 (+46%) 🔻 498 🔴 (+148%) 🔻 878 🔴 (+269%) 🔻 2454 🔴 (+241%) 🔻 30
📈 STSO distribution vs main (inline / queue-hop histograms)

1020 steps (inline)

Cumulative STSO time: main 388501ms → this run 413359ms (Δ +24858ms, +6%)

  150-200 ms  ██┃███                    main  44  this  21   -23
  200-250 ms  ████████████┃███          main 107  this  89   -18
  250-300 ms  ████████████████┃██       main 132  this 118   -14
  300-350 ms  █████████████████┃        main 123  this 123    +0
  350-400 ms  █████████████████████┃██  main 162  this 153    -9
  400-450 ms  ██████████████████████░┃  main 150  this 165   +15
  450-500 ms  ████████████████████┃     main 142  this 147    +5
  500-550 ms  █████████████░░┃          main  89  this 109   +20
  550-600 ms  █████░░┃                  main  31  this  52   +21
  600-650 ms  ██┃                       main  24  this  18    -6
  650-700 ms  ┃                         main   4  this   9    +5
  700-750 ms  ┃                         main   3  this   7    +4
  750-800 ms  ┃                         main   1  this   1    +0
  800-850 ms  ┃                         main   1  this   3    +2
  850-900 ms  ┃                         main   1  this   1    +0
  900-950 ms  ┃                         main   1  this   0    -1
1050-1100 ms  ┃                         main   1  this   0    -1
1700-1750 ms  ┃                         main   0  this   1    +1
2500-2550 ms  ┃                         main   0  this   1    +1
2550-2600 ms  ┃                         main   0  this   1    +1
ℹ️ Metric definitions & methodology

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

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

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

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

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

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

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR isolates the behavioral change from #3198 on top of a known-good base to help bisect the “hook-storm” regression, by adjusting the core idle-check logic so it does not observe “idle” while a committed (self-resolving) delivery is still parked between its queue slot and its detached resolve() continuation.

Changes:

  • Add an idle-check guard that also considers the delivery-barrier registry (not just pendingDeliveries) to prevent premature WorkflowSuspension while deliveries are parked mid-deferral.
  • Document the required invariant for armed delivery barriers (armed barriers must have an unconditional delivery chain).
  • Add regression tests covering the suspension timing case (parallel steps + fire-and-forget sleep), and add a changeset for the core patch.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
packages/core/src/private.ts Extends scheduleWhenIdle to wait out parked committed deliveries using the barrier registry; documents the barrier invariant.
packages/core/src/delivery-barrier-coverage.test.ts Adds suspension-timing regression coverage reproducing the parked-delivery/idle race and a serial control.
.changeset/idle-check-parked-deliveries.md Records the @workflow/core patch-level change for release notes/versioning.
Comments suppressed due to low confidence (1)

packages/core/src/delivery-barrier-coverage.test.ts:660

  • Same as above: prefix this fire-and-forget sleep() with void so it’s clearly intentional and doesn’t trip floating-promise linting (consistent with other tests, e.g. packages/core/src/abort-consistency.test.ts:634-642).
      sleep('25m').catch(() => {});

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


const error = await replay(ctx, async () => {
// Watchdog: arms the idle check on every replay, completes on none.
sleep('25m').catch(() => {});
@github-actions

Copy link
Copy Markdown
Contributor

Event Log Race Repro

1138 of 1400 latest repro runs hit event-log regressions. 2 runs hit harness-side infra outcomes (hook-resume timing races / transport errors); these are reported but do not fail the job.

Run History

Metric 2026-07-30 23:12 UTC #1
logs / deploy
Result 1138/1400 regressions (+2 infra)
Total 1400
completed 260
CORRUPTED_EVENT_LOG 1130
USER_ERROR 0
RUNTIME_ERROR 0
stuck 8
other 0
infra 2
Config 1400 runs / step-storm 600, hook-storm 600, hook-sleep 200 / c40 / 6x8
Timing watchdog 2500ms / step 2200±250ms / stagger 400ms / poke 750ms / timeout 240000ms

Latest Scenario Breakdown

Scenario Total completed CORRUPTED_EVENT_LOG USER_ERROR RUNTIME_ERROR stuck other infra
step-storm 600 59 539 0 0 0 0 2
hook-storm 600 1 591 0 0 8 0 0
hook-sleep 200 200 0 0 0 0 0 0

Latest Non-Completed Runs

Scenario Attempt Outcome Status Error code Run
step-storm 39 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK883M0GPYBGTCVAE1ZBV1
step-storm 36 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK889D0GKB9E1NKTHXRV3R
step-storm 15 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87HC0GN7QH7X0Z0B9NQC
step-storm 7 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87EQ0GZBB3BQ1JNZB0BG
step-storm 13 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87H60GQDGQ9AWXWCQSDS
step-storm 18 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87JH0GGE6CWSJ1E477XP
step-storm 16 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87HE0GS5KH8B9T33VYB4
step-storm 3 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87ER0GZ8WFCQ0PB6Q990
step-storm 1 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87EP0GZT6QCYGJ0MXS52
step-storm 40 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK883C0GWWGVDKQZ2RQZWZ
step-storm 2 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87EQ0GZBB3BQ1JNZB0BF
step-storm 19 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87K30GNQX60G90CT0SDP
step-storm 22 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87M80GKM2JAD1GT5RFAP
step-storm 21 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87MN0GNJDEJNEJCEVSVJ
step-storm 5 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87ER0GZ8WFCQ0PB6Q993
step-storm 12 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87GW0GM04984AEQF8FHE
step-storm 34 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK88230GV3592QA9MG3F5S
step-storm 32 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK880J0GYQ1GBAFYQA4YS9
step-storm 6 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87ER0GZ8WFCQ0PB6Q992
step-storm 8 CORRUPTED_EVENT_LOG failed CORRUPTED_EVENT_LOG wrun_41KYTK87ER0GZ8WFCQ0PB6Q991

Showing 20 of 1140 non-completed runs.

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

Labels

None yet

4 participants