Skip to content

[world-local] Keep other tags' lock files in a tagged clear() - #4742

Merged
pranaygp merged 1 commit into
mainfrom
pgp/world-local-tagged-clear-locks
Oct 8, 2026
Merged

pranaygp merged 1 commit into
mainfrom
pgp/world-local-tagged-clear-locks

Conversation

@pranaygp

@pranaygp pranaygp commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Fixes the Vitest Plugin Tests (node) failure on main (hook-token-reuse.test.ts › "same run can recreate a hook with the same token after dispose()", e.g. https://github.com/vercel/workflow/actions/runs/37694829663/job/113044138956).

Cause

A tagged clear() ran rm -rf <dataDir>/.locks, deleting every tag's locks, not only its own. Hook dispose locks live there (.locks/hooks/<hookId>.disposed.<tag>).

@workflow/vitest gives each worker its own tag on one shared data directory and calls clear() whenever a worker starts a test file. So a worker starting a file could delete the dispose lock of a hook another worker's run had just disposed. For round N of reuseHookTokenWorkflow:

  1. hook_disposed for the previous round's hook writes its dispose lock and releases the token claim.
  2. hook_created for the new hook finds no claim and rebuilds it from the event log.
  3. The rebuild treats a hook_created as live unless its dispose lock exists. With the lock deleted, an earlier disposed hook of the same run counts as live and gets its claim restored.
  4. That claim belongs to a different hook of the same run, the run isn't terminal, and it has no lock, so it isn't releasable. The new hook gets a hook_conflict against its own run.

Round 2 is where it usually fails: round 0's lock can be deleted any time during round 1, while a round 1 failure needs the delete to land between two back-to-back writes.

#4718 didn't introduce this. It made clear() list only its own runs' directories instead of the whole shared events/ and steps/, so the .locks delete runs sooner after a worker starts a file. On the 4-vCPU runner that moved it into this test's window. Before #4718 merged, 0 of 8 node runs failed; after, nearly every node run did, always this test. It doesn't reproduce locally when there are enough cores to start every file (and its clear()) at once.

Fix

A tagged clear() now deletes only .locks/<kind>/*.<tag> entries, including the <runId>.pending.<tag> staging directories. Every tagged lock name ends in its tag. This matches the documented contract that a tagged clear() only deletes its own tag's files.

Verification

  • New tag.test.ts cases: a tagged clear() keeps other tags' lock files, and doesn't turn another tag's dispose-then-recreate into a hook_conflict. Both fail on main; the second fails with the same hook_conflict.
  • Repro of the CI failure: running hook-token-reuse.test.ts with a second tagged world calling clear() every 20ms fails 3/3 on main with round 2 should register a hook, got {"value":"conflict-round-2:…"}. With this change it passes 5/5 (node and quickjs).
  • packages/world-local: 630/630 tests pass and typecheck is clean. workbench/vitest passes on both WORKFLOW_VM=node and quickjs.

🤖 Generated with Claude Code

A tagged `clear()` removed the whole shared `.locks` directory, including
other tags' hook dispose locks. Every vitest worker clears under its own
tag when a test file starts, so a worker starting a file could delete the
dispose lock of a hook another worker's run had just disposed. Without its
lock the disposed hook looks live to the token claim rebuild, and the run
recreating the same token then gets a `hook_conflict` against itself
(workbench/vitest hook-token-reuse.test.ts, "same run can recreate a hook
with the same token after dispose()").

Delete only lock files ending in this world's tag.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@pranaygp
pranaygp requested a review from a team as a code owner October 8, 2026 03:36
Copilot AI balanced review requested due to automatic review settings October 8, 2026 03:36
@changeset-bot

changeset-bot Bot commented Oct 8, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9f54316

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

This PR includes changesets to release 19 packages
Name Type
@workflow/world-local Patch
@workflow/cli Patch
@workflow/core Patch
@workflow/vitest Patch
@workflow/web Patch
@workflow/world-postgres Patch
workflow Patch
@workflow/world-testing Patch
@workflow/builders Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/web-shared Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch
@workflow/nuxt Patch
@workflow/world-vercel 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

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@vercel

vercel Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated
example-nextjs-workflow-turbopack Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
example-nextjs-workflow-webpack Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
example-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-astro-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-express-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-fastify-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-hono-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-nestjs-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-nitro-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-nuxt-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-python-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-sveltekit-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-tanstack-start-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workbench-vite-workflow Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workflow-docs Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workflow-swc-playground Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workflow-tarballs Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC
workflow-web Ready Ready Preview, v0 Oct 8, 2026 3:38am UTC

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

🧪 E2E Test Results

✅ All tests passed

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

  • run-pickup-stall · runs app-generated source against a registered step (tanstack-start) · at 03:41:14Z · abandoned wrun_01M4CSK7280EVZWA0PP1D9JJGB
  • cold-start-warmup · suite warmup (tanstack-start) · at 03:41:15Z · abandoned wrun_01M4CSK729X4C9AG3YQ9DF7564
  • run-pickup-stall · cancelRun via CLI - cancelling a running workflow (nextjs-webpack) · at 03:47:56Z · abandoned wrun_01M4CSZFQHJTD3437871RZXTVQ

E2E Test Summary

Summary
Passed Failed Skipped Total
✅ ▲ Vercel Production 3982 0 878 4860
✅ 💻 Local Development 4669 0 551 5220
✅ 📦 Local Production 4669 0 551 5220
✅ 🐘 Local Postgres 4669 0 551 5220
✅ 🪟 Windows 348 0 12 360
✅ 🌐 Cross-language Conformance 68 0 87 155
✅ dynamic-runs 0 0 0 0
✅ vercel-http-transport 897 0 183 1080
✅ vercel-multi-region 27 0 0 27
✅ vercel-ws-transport 607 0 113 720
Total 19936 0 2926 22862
Details by Category

✅ ▲ Vercel Production

App Passed Failed Skipped
✅ astro-node 145 0 35
✅ astro-quickjs 145 0 35
✅ example-node 145 0 35
✅ example-quickjs 145 0 35
✅ express-node 145 0 35
✅ express-quickjs 145 0 35
✅ fastify-node 145 0 35
✅ fastify-quickjs 145 0 35
✅ hono-node 145 0 35
✅ hono-quickjs 145 0 35
✅ nest-node 145 0 35
✅ nest-quickjs 145 0 35
✅ nextjs-turbopack-node 172 0 8
✅ nextjs-turbopack-quickjs 172 0 8
✅ nextjs-webpack-node 172 0 8
✅ nextjs-webpack-quickjs 172 0 8
✅ nitro-node 145 0 35
✅ nitro-quickjs 145 0 35
✅ nuxt-node 145 0 35
✅ nuxt-quickjs 145 0 35
✅ python-node 66 0 114
✅ sveltekit-node 164 0 16
✅ sveltekit-quickjs 164 0 16
✅ tanstack-start-node 145 0 35
✅ tanstack-start-quickjs 145 0 35
✅ vite-node 145 0 35
✅ vite-quickjs 145 0 35

✅ 💻 Local Development

App Passed Failed Skipped
✅ astro-stable-node 151 0 29
✅ astro-stable-quickjs 151 0 29
✅ express-stable-node 151 0 29
✅ express-stable-quickjs 151 0 29
✅ fastify-stable-node 151 0 29
✅ fastify-stable-quickjs 151 0 29
✅ hono-stable-node 151 0 29
✅ hono-stable-quickjs 151 0 29
✅ nest-stable-node 151 0 29
✅ nest-stable-quickjs 151 0 29
✅ nextjs-turbopack-canary-node 179 0 1
✅ nextjs-turbopack-canary-quickjs 179 0 1
✅ nextjs-turbopack-quickjs-snapshot 179 0 1
✅ nextjs-turbopack-stable-node 179 0 1
✅ nextjs-turbopack-stable-quickjs 179 0 1
✅ nextjs-webpack-canary-node 179 0 1
✅ nextjs-webpack-canary-quickjs 179 0 1
✅ nextjs-webpack-stable-node 179 0 1
✅ nextjs-webpack-stable-quickjs 179 0 1
✅ nitro-stable-node 151 0 29
✅ nitro-stable-quickjs 151 0 29
✅ nuxt-stable-node 151 0 29
✅ nuxt-stable-quickjs 151 0 29
✅ sveltekit-stable-node 170 0 10
✅ sveltekit-stable-quickjs 170 0 10
✅ tanstack-start-node 151 0 29
✅ tanstack-start-quickjs 151 0 29
✅ vite-stable-node 151 0 29
✅ vite-stable-quickjs 151 0 29

✅ 📦 Local Production

App Passed Failed Skipped
✅ astro-stable-node 151 0 29
✅ astro-stable-quickjs 151 0 29
✅ express-stable-node 151 0 29
✅ express-stable-quickjs 151 0 29
✅ fastify-stable-node 151 0 29
✅ fastify-stable-quickjs 151 0 29
✅ hono-stable-node 151 0 29
✅ hono-stable-quickjs 151 0 29
✅ nest-stable-node 151 0 29
✅ nest-stable-quickjs 151 0 29
✅ nextjs-turbopack-canary-node 179 0 1
✅ nextjs-turbopack-canary-quickjs 179 0 1
✅ nextjs-turbopack-quickjs-snapshot 179 0 1
✅ nextjs-turbopack-stable-node 179 0 1
✅ nextjs-turbopack-stable-quickjs 179 0 1
✅ nextjs-webpack-canary-node 179 0 1
✅ nextjs-webpack-canary-quickjs 179 0 1
✅ nextjs-webpack-stable-node 179 0 1
✅ nextjs-webpack-stable-quickjs 179 0 1
✅ nitro-stable-node 151 0 29
✅ nitro-stable-quickjs 151 0 29
✅ nuxt-stable-node 151 0 29
✅ nuxt-stable-quickjs 151 0 29
✅ sveltekit-stable-node 170 0 10
✅ sveltekit-stable-quickjs 170 0 10
✅ tanstack-start-node 151 0 29
✅ tanstack-start-quickjs 151 0 29
✅ vite-stable-node 151 0 29
✅ vite-stable-quickjs 151 0 29

✅ 🐘 Local Postgres

App Passed Failed Skipped
✅ astro-stable-node 151 0 29
✅ astro-stable-quickjs 151 0 29
✅ express-stable-node 151 0 29
✅ express-stable-quickjs 151 0 29
✅ fastify-stable-node 151 0 29
✅ fastify-stable-quickjs 151 0 29
✅ hono-stable-node 151 0 29
✅ hono-stable-quickjs 151 0 29
✅ nest-stable-node 151 0 29
✅ nest-stable-quickjs 151 0 29
✅ nextjs-turbopack-canary-node 179 0 1
✅ nextjs-turbopack-canary-quickjs 179 0 1
✅ nextjs-turbopack-quickjs-snapshot 179 0 1
✅ nextjs-turbopack-stable-node 179 0 1
✅ nextjs-turbopack-stable-quickjs 179 0 1
✅ nextjs-webpack-canary-node 179 0 1
✅ nextjs-webpack-canary-quickjs 179 0 1
✅ nextjs-webpack-stable-node 179 0 1
✅ nextjs-webpack-stable-quickjs 179 0 1
✅ nitro-stable-node 151 0 29
✅ nitro-stable-quickjs 151 0 29
✅ nuxt-stable-node 151 0 29
✅ nuxt-stable-quickjs 151 0 29
✅ sveltekit-stable-node 170 0 10
✅ sveltekit-stable-quickjs 170 0 10
✅ tanstack-start-node 151 0 29
✅ tanstack-start-quickjs 151 0 29
✅ vite-stable-node 151 0 29
✅ vite-stable-quickjs 151 0 29

✅ 🪟 Windows

App Passed Failed Skipped
✅ nextjs-turbopack-node 174 0 6
✅ nextjs-turbopack-quickjs 174 0 6

✅ 🌐 Cross-language Conformance

App Passed Failed Skipped
✅ python 68 0 87

✅ dynamic-runs

App Passed Failed Skipped
✅ astro-vercel 0 0 0
✅ example-vercel 0 0 0
✅ express-vercel 0 0 0
✅ fastify-vercel 0 0 0
✅ hono-vercel 0 0 0
✅ nest-vercel 0 0 0
✅ nextjs-turbopack-vercel 0 0 0
✅ nextjs-webpack-vercel 0 0 0
✅ nitro-vercel 0 0 0
✅ nuxt-vercel 0 0 0
✅ sveltekit-vercel 0 0 0
✅ tanstack-start-vercel 0 0 0
✅ vite-vercel 0 0 0

✅ vercel-http-transport

App Passed Failed Skipped
✅ example 145 0 35
✅ express 145 0 35
✅ hono 145 0 35
✅ nextjs-turbopack 172 0 8
✅ nitro 145 0 35
✅ vite 145 0 35

✅ vercel-multi-region

App Passed Failed Skipped
✅ nextjs-turbopack 27 0 0

✅ vercel-ws-transport

App Passed Failed Skipped
✅ example 145 0 35
✅ express 145 0 35
✅ nextjs-turbopack 172 0 8
✅ vite 145 0 35

📋 View full workflow run

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

📊 Workflow Benchmarks

commit 9f54316 · Thu, 08 Oct 2026 03:50:52 GMT · run logs

Backend: vercel · app: nextjs-turbopack

Metric Scenario Best (ms) P75 (ms) P90 (ms) P99 (ms) Samples
TTFS step 1775 (+624%) 🔻 1951 🔴 (+19%) 🔻 1990 🔴 (+19%) 🔻 2403 🔴 (+28%) 🔻 30
TTFS stream 1845 (+557%) 🔻 1960 🔴 (+37%) 🔻 1984 🔴 (+31%) 🔻 2026 🔴 (+21%) 🔻 30
TTFS hook + stream 2091 (+317%) 🔻 2283 🔴 (+31%) 🔻 2304 🔴 (+22%) 🔻 2938 🔴 (+48%) 🔻 30
Fan-out TTFS Promise.all(100 steps) 594 (+12%) 767 (-18%) 💚 809 (-17%) 💚 2075 (+12%) 10
Fan-out TTLS Promise.all(100 steps) 2635 (+77%) 🔻 2911 (-61%) 💚 4255 (-45%) 💚 7410 (-12%) 10
STSO 1020 steps (inline) 83 (-1.2%) 99 (-13%) 110 (-21%) 💚 187 (-31%) 💚 1019
WO 1020 steps 99775 (-13%) 99775 (-13%) 99775 (-13%) 99775 (-13%) 1
📈 STSO distribution vs main (inline / queue-hop histograms)

1020 steps (inline)

Cumulative STSO time: main 115030ms → this run 99503ms (Δ -15527ms, -13%)

 50-100 ms  █████████████░░░░░░░░░░┃  main 411  this 776  +365
100-150 ms  ██████┃█████████          main 530  this 228  -302
150-200 ms  ┃█                        main  49  this   5   -44
200-250 ms  ┃                         main  13  this   3   -10
250-300 ms  ┃                         main   8  this   6    -2
300-350 ms  ┃                         main   3  this   1    -2
350-400 ms  ┃                         main   2  this   0    -2
450-500 ms  ┃                         main   2  this   0    -2
600-650 ms  ┃                         main   1  this   0    -1
ℹ️ Metric definitions & methodology

The collapsed STSO distribution section above buckets every step gap, split inline (same warm process — pure framework overhead) vs queue-hop (fresh process — dispatch, reinit, replay). █ = main, ┃ = this run, ░ = fill.

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) · Fan-out TTFS: fan-out time to first step (in-deployment start() → first of the parallel step bodies to complete) · Fan-out TTLS: fan-out time to last step (in-deployment start() → last of the parallel step bodies to complete, i.e. when the Promise.all resolves) · STSO: step-to-step overhead (gap between consecutive step bodies) · WO: workflow overhead (whole-run time outside step bodies, in-deployment anchored)

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 · Promise.all(100 steps): 100 trivial no-op steps started together in a single Promise.all; Fan-out TTFS is the first of them to complete and Fan-out TTLS the last, both from the in-deployment clientStart, so their gap is the spread the runtime adds across the fan-out

🔴 marks a percentile over its target (within target is left unmarked). Targets (p75/p90/p99, ms) — TTFS 200/300/600

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. Streaming delivery performance is measured in durabench.

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

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Sim World

Simulated world deterministic testing for races. Traces

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

fence=per-spec

scenario outcome events virt replay violations
✅ smoke-no-steps completed 3 0ms ok 0
✅ smoke-one-step completed 6 0ms ok 0
✅ hook-at-step-started completed 12 0ms ok 0
✅ hook-at-step-completed completed 12 0ms ok 0
✅ hook-at-hook-created completed 12 0ms ok 0
✅ deadline-hook-wins completed 7 1.0h ok 0
✅ deadline-expires completed 7 1.0h ok 0
✅ step-vs-timer-early-settlement completed 8 1.0h ok 0
✅ long-sleep completed 11 30.0d ok 0
✅ hook-never-arrives stalled 3 0ms skipped 0
✅ step-retries-twice completed 10 2.0s ok 0
✅ parallel-steps completed 9 0ms ok 0
✅ hook-on-execution-state completed 12 0ms ok 0
✅ peek-hook-before-branch completed 12 0ms ok 0
✅ peek-hook-after-branch completed 12 0ms ok 0
✅ peek-hook-at-registration completed 12 0ms ok 0
✅ race-hook-before-probe completed 12 0ms ok 0
✅ race-hook-after-probe completed 12 0ms ok 0
✅ race-duplicate-delivery completed 13 0ms ok 0
✅ attr-hook-before-step completed 11 0ms ok 0
✅ attr-hook-after-step completed 11 0ms ok 0
✅ attr-from-step-body completed 13 0ms ok 0
✅ fork-hook-after-timeout completed 14 1.0m ok 0
✅ fork-hook-before-timeout completed 14 1.0m ok 0
✅ count-hook-after-timeout completed 17 1.0m ok 0
✅ count-hook-before-timeout completed 20 1.0m ok 0
✅ stale-read-step-count-fork completed 20 1.0m ok 0
✅ stale-read-equal-step-counts completed 14 1.0m ok 0
✅ step-vs-step-fork completed 12 0ms ok 0
✅ step-vs-step-fork-fenced completed 12 0ms ok 0
✅ fence-catches-benign-direction completed 12 5ms ok 0
✅ in-flight-before-decision completed 17 1.0m ok 0
❌ in-flight-before-decision-counted completed 17 1.0m ok 0
✅ in-flight-after-decision completed 19 2.0m ok 0
✅ stale-read-step-count-fork-fenced completed 20 1.0m ok 0
✅ fork-hook-wins completed 13 1.0m ok 0
✅ fork-timeout-wins completed 13 1.0m ok 0
✅ unclaimed-payload-under-fork completed 17 1.0m ok 0
✅ claimed-payload-under-fork completed 17 1.0m ok 0
✅ writers-independent-step-bodies completed 12 0ms ok 0
✅ writers-scripted-tempo completed 12 0ms ok 0
✅ cancel-mid-step cancelled 7 0ms skipped 0

Full trace: world-sim.txt

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor
Framework Flow route Step reg. Framework output
hono 267.4 KiB (±0) 95.4 KiB (±0) 1.98 MiB (+392 B)
nextjs-turbopack 274.8 KiB (±0) 426 B (±0) 968.1 KiB (+84 B)
About these numbers

Sizes are gzip; parentheses show the change against main.
Flow route and Step reg. gate this job, on raw bytes rather than the gzip shown, at max(2%, 50.0 KiB). Framework output is informational.

9f54316 · run

@pranaygp
pranaygp enabled auto-merge (squash) October 8, 2026 03:43
@pranaygp
pranaygp merged commit b38360d into main Oct 8, 2026
188 checks passed
@pranaygp
pranaygp deleted the pgp/world-local-tagged-clear-locks branch October 8, 2026 03:54
@github-actions github-actions Bot mentioned this pull request Oct 8, 2026
github-actions Bot added a commit that referenced this pull request Oct 8, 2026
Fixes the `Vitest Plugin Tests (node)` failure on `main`
(`hook-token-reuse.test.ts` › "same run can recreate a hook with the
same token after dispose()", e.g.
https://github.com/vercel/workflow/actions/runs/37694829663/job/113044138956).

A tagged `clear()` ran `rm -rf <dataDir>/.locks`, deleting every tag's
locks, not only its own. Hook dispose locks live there
(`.locks/hooks/<hookId>.disposed.<tag>`).

`@workflow/vitest` gives each worker its own tag on one shared data
directory and calls `clear()` whenever a worker starts a test file. So a
worker starting a file could delete the dispose lock of a hook another
worker's run had just disposed. For round N of `reuseHookTokenWorkflow`:

1. `hook_disposed` for the previous round's hook writes its dispose lock
and releases the token claim.
2. `hook_created` for the new hook finds no claim and rebuilds it from
the event log.
3. The rebuild treats a `hook_created` as live unless its dispose lock
exists. With the lock deleted, an earlier disposed hook of the same run
counts as live and gets its claim restored.
4. That claim belongs to a different hook of the same run, the run isn't
terminal, and it has no lock, so it isn't releasable. The new hook gets
a `hook_conflict` against its own run.

Round 2 is where it usually fails: round 0's lock can be deleted any
time during round 1, while a round 1 failure needs the delete to land
between two back-to-back writes.

directories instead of the whole shared `events/` and `steps/`, so the
`.locks` delete runs sooner after a worker starts a file. On the 4-vCPU
runner that moved it into this test's window. Before #4718 merged, 0 of
8 node runs failed; after, nearly every node run did, always this test.
It doesn't reproduce locally when there are enough cores to start every
file (and its `clear()`) at once.

A tagged `clear()` now deletes only `.locks/<kind>/*.<tag>` entries,
including the `<runId>.pending.<tag>` staging directories. Every tagged
lock name ends in its tag. This matches the documented contract that a
tagged `clear()` only deletes its own tag's files.

- New `tag.test.ts` cases: a tagged `clear()` keeps other tags' lock
files, and doesn't turn another tag's dispose-then-recreate into a
`hook_conflict`. Both fail on `main`; the second fails with the same
`hook_conflict`.
- Repro of the CI failure: running `hook-token-reuse.test.ts` with a
second tagged world calling `clear()` every 20ms fails 3/3 on `main`
with `round 2 should register a hook, got
{"value":"conflict-round-2:…"}`. With this change it passes 5/5 (node
and quickjs).
- `packages/world-local`: 630/630 tests pass and typecheck is clean.
`workbench/vitest` passes on both `WORKFLOW_VM=node` and `quickjs`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Backport PR opened against stable: #4743. Merge conflicts were resolved by AI — please review carefully. (backport job run)

pranaygp pushed a commit that referenced this pull request Oct 8, 2026
… clear() (#4743)

Automated backport of #4742 to `stable` ([backport job
run](https://github.com/vercel/workflow/actions/runs/37724968628)).

**AI recommendation:** This is a pure bug fix (patch changeset) to
`@workflow/world-local`'s tagged `clear()`, which deleted the whole
shared `.locks` directory instead of only its own tag's lock files,
causing another tag's disposed hooks to be resurrected and produce
spurious `hook_conflict` errors. I verified
`origin/stable:packages/world-local/src/index.ts` contains the identical
`fs.rm(path.join(basedir, '.locks'), ...)` call inside the tagged
`clear()` branch, so the defect — and the resulting CI flakiness —
exists on `stable` too. The change adds no new API surface, only a
correctness fix plus regression tests in the existing `tag.test.ts`.

Merge conflicts were resolved by AI ([opencode](https://opencode.ai)
with `anthropic/claude-opus-5`). **Please review the conflict resolution
carefully before merging.**

Signed-off-by: Pranay Prakash <pranay.gp@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
pranaygp pushed a commit that referenced this pull request Oct 8, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @workflow/world-local@5.1.0

### Minor Changes

- [#4718](#4718)
[`0139561`](0139561)
Thanks [@ruiconti](https://github.com/ruiconti)! - Store event and step
files in one directory per run for faster reads. Local run data written
by earlier releases is deleted on upgrade.

### Patch Changes

- [#4742](#4742)
[`b38360d`](b38360d)
Thanks [@pranaygp](https://github.com/pranaygp)! - Make a tagged
`clear()` delete only its own tag's lock files, so it no longer reopens
another tag's disposed hooks mid-run.
- Updated dependencies
[[`46b3bfb`](46b3bfb)]:
  - @workflow/world@5.0.3
  - @workflow/errors@5.0.3
## @workflow/world-vercel@5.2.0

### Minor Changes

- [#4722](#4722)
[`d0f6b91`](d0f6b91)
Thanks [@alangenfeld](https://github.com/alangenfeld)! - Stream writes
now upgrade to WebSocket by default; set
`WORKFLOW_STREAMS_TRANSPORT=http` to opt out.

### Patch Changes

- [#4712](#4712)
[`46b3bfb`](46b3bfb)
Thanks [@karthikscale3](https://github.com/karthikscale3)! - Reduce
redundant schema compilation by compiling final event schemas only and
reusing compiled v4 event-response and paginated response schemas.
- Updated dependencies
[[`46b3bfb`](46b3bfb)]:
  - @workflow/world@5.0.3
  - @workflow/errors@5.0.3
## @workflow/astro@5.0.3

### Patch Changes

- Updated dependencies
[[`1acc0ba`](1acc0ba)]:
  - @workflow/swc-plugin@5.0.1
  - @workflow/builders@5.0.3
  - @workflow/rollup@5.0.3
  - @workflow/vite@5.0.3
## @workflow/builders@5.0.3

### Patch Changes

- Updated dependencies
[[`de8d985`](de8d985),
[`306a40f`](306a40f),
[`515dc9f`](515dc9f),
[`1acc0ba`](1acc0ba),
[`878979b`](878979b),
[`3949c07`](3949c07),
[`5589df7`](5589df7)]:
  - @workflow/core@5.2.0
  - @workflow/swc-plugin@5.0.1
  - @workflow/errors@5.0.3
## @workflow/cli@5.0.3

### Patch Changes

- [#4376](#4376)
[`2036247`](2036247)
Thanks [@withkarann](https://github.com/withkarann)! - Ship an oclif
command manifest so the CLI loads only the command being run, and skip
loading the runtime on exit when no World was created. `workflow
--version` starts in about 50 ms instead of 650 ms.
- Updated dependencies
[[`de8d985`](de8d985),
[`306a40f`](306a40f),
[`46b3bfb`](46b3bfb),
[`515dc9f`](515dc9f),
[`1acc0ba`](1acc0ba),
[`878979b`](878979b),
[`3949c07`](3949c07),
[`5589df7`](5589df7),
[`28cb70d`](28cb70d),
[`0139561`](0139561),
[`b38360d`](b38360d),
[`d0f6b91`](d0f6b91)]:
  - @workflow/core@5.2.0
  - @workflow/world-vercel@5.2.0
  - @workflow/world@5.0.3
  - @workflow/swc-plugin@5.0.1
  - @workflow/web@5.0.3
  - @workflow/world-local@5.1.0
  - @workflow/builders@5.0.3
  - @workflow/errors@5.0.3
## @workflow/core@5.2.0

### Patch Changes

- [#4727](#4727)
[`de8d985`](de8d985)
Thanks [@pranaygp](https://github.com/pranaygp)! - Start the queued
branches of a wide `Promise.all` fan-out sooner. The batched fan-out now
commits its writes in `createBatch` calls of at most 16 events instead
of 32. Each queued branch's message waits on its chunk's commit and then
its chunk's batched publish, and both are faster for smaller chunks. In
production sweeps, the last branch of a 64-branch fan-out started about
90 ms sooner at p50, and of a 128-branch fan-out about 200 ms sooner,
with no change to the first branch at those widths.

- [#4324](#4324)
[`306a40f`](306a40f)
Thanks [@VaguelySerious](https://github.com/VaguelySerious)! - Deliver a
hook payload to the pending await when an earlier `Promise.race` over
the same hook was lost
  
Concurrent awaits of one hook, such as `Promise.all([hook, hook])`, may
now receive the same payload. Runs that already took the timeout branch
in this situation may fail to replay after upgrading in place.

- [#4438](#4438)
[`515dc9f`](515dc9f)
Thanks [@TooTallNate](https://github.com/TooTallNate)! - Reject
lifecycle registration inside steps, preserve reporting diagnostics for
unreadable errors, and document executor registration and
stream-lifetime constraints.

- [#4690](#4690)
[`878979b`](878979b)
Thanks [@TooTallNate](https://github.com/TooTallNate)! - Enable
VM-memory snapshotting by default for the QuickJS engine, with a
threshold of 1000 events. Set `WORKFLOW_SNAPSHOT_THRESHOLD=0` to opt
out. Runs without an encryption key (for example on world-local and
world-postgres) are still not snapshotted unless
`WORKFLOW_SNAPSHOT_ALLOW_UNENCRYPTED=1` is set.

- [#4683](#4683)
[`3949c07`](3949c07)
Thanks [@VaguelySerious](https://github.com/VaguelySerious)! - Record on
the invocation trace whether out-of-band events could affect each inline
step boundary

- [#4685](#4685)
[`5589df7`](5589df7)
Thanks [@pranaygp](https://github.com/pranaygp)! - Hold a turbo step's
awaited `step_started` until the backgrounded `run_started` lands, so an
explicit `WORKFLOW_OPTIMISTIC_INLINE_START=0` no longer gets its first
step rejected for a run that has not started yet.
- Updated dependencies
[[`46b3bfb`](46b3bfb),
[`0139561`](0139561),
[`b38360d`](b38360d),
[`d0f6b91`](d0f6b91)]:
  - @workflow/world-vercel@5.2.0
  - @workflow/world@5.0.3
  - @workflow/world-local@5.1.0
  - @workflow/errors@5.0.3
## @workflow/errors@5.0.3

### Patch Changes

- Updated dependencies
[[`46b3bfb`](46b3bfb)]:
  - @workflow/world@5.0.3
## @workflow/nest@5.1.1

### Patch Changes

- Updated dependencies
[[`1acc0ba`](1acc0ba)]:
  - @workflow/swc-plugin@5.0.1
  - @workflow/builders@5.0.3
## @workflow/next@5.0.3

### Patch Changes

- Updated dependencies
[[`de8d985`](de8d985),
[`306a40f`](306a40f),
[`515dc9f`](515dc9f),
[`1acc0ba`](1acc0ba),
[`878979b`](878979b),
[`3949c07`](3949c07),
[`5589df7`](5589df7)]:
  - @workflow/core@5.2.0
  - @workflow/swc-plugin@5.0.1
  - @workflow/builders@5.0.3
## @workflow/nitro@5.0.3

### Patch Changes

- Updated dependencies
[[`de8d985`](de8d985),
[`306a40f`](306a40f),
[`515dc9f`](515dc9f),
[`1acc0ba`](1acc0ba),
[`878979b`](878979b),
[`3949c07`](3949c07),
[`5589df7`](5589df7),
[`28cb70d`](28cb70d)]:
  - @workflow/core@5.2.0
  - @workflow/swc-plugin@5.0.1
  - @workflow/web@5.0.3
  - @workflow/builders@5.0.3
  - @workflow/rollup@5.0.3
  - @workflow/vite@5.0.3
## @workflow/nuxt@5.0.3

### Patch Changes

- Updated dependencies []:
  - @workflow/nitro@5.0.3
## @workflow/rollup@5.0.3

### Patch Changes

- Updated dependencies
[[`1acc0ba`](1acc0ba)]:
  - @workflow/swc-plugin@5.0.1
  - @workflow/builders@5.0.3
## @workflow/sveltekit@5.0.3

### Patch Changes

- Updated dependencies
[[`1acc0ba`](1acc0ba)]:
  - @workflow/swc-plugin@5.0.1
  - @workflow/builders@5.0.3
  - @workflow/rollup@5.0.3
  - @workflow/vite@5.0.3
## @workflow/swc-plugin@5.0.1

### Patch Changes

- [#4615](#4615)
[`1acc0ba`](1acc0ba)
Thanks [@karthikscale3](https://github.com/karthikscale3)! - Stabilize
nested step IDs across scopes and SWC transform modes. Self-hosted runs
that already recorded one of the previously colliding IDs may report a
replay divergence if they continue on upgraded code; restart those
in-flight runs after upgrading.
## @workflow/vite@5.0.3

### Patch Changes

- Updated dependencies []:
  - @workflow/builders@5.0.3
## @workflow/vitest@5.0.3

### Patch Changes

- Updated dependencies
[[`de8d985`](de8d985),
[`306a40f`](306a40f),
[`46b3bfb`](46b3bfb),
[`515dc9f`](515dc9f),
[`878979b`](878979b),
[`3949c07`](3949c07),
[`5589df7`](5589df7),
[`0139561`](0139561),
[`b38360d`](b38360d)]:
  - @workflow/core@5.2.0
  - @workflow/world@5.0.3
  - @workflow/world-local@5.1.0
  - @workflow/builders@5.0.3
  - @workflow/rollup@5.0.3
## @workflow/web@5.0.3

### Patch Changes

- [#4736](#4736)
[`28cb70d`](28cb70d)
Thanks [@TooTallNate](https://github.com/TooTallNate)! - Upgrade
`react-router` and `@react-router/*` to 7.18.4 to address CVE-2026-34077
and GHSA-qwww-vcr4-c8h2
- Updated dependencies
[[`0139561`](0139561),
[`b38360d`](b38360d)]:
  - @workflow/world-local@5.1.0
## @workflow/web-shared@5.0.3

### Patch Changes

- Updated dependencies
[[`de8d985`](de8d985),
[`306a40f`](306a40f),
[`46b3bfb`](46b3bfb),
[`515dc9f`](515dc9f),
[`878979b`](878979b),
[`3949c07`](3949c07),
[`5589df7`](5589df7)]:
  - @workflow/core@5.2.0
  - @workflow/world@5.0.3
## workflow@5.2.0

### Patch Changes

- [#4438](#4438)
[`515dc9f`](515dc9f)
Thanks [@TooTallNate](https://github.com/TooTallNate)! - Reject
lifecycle registration inside steps, preserve reporting diagnostics for
unreadable errors, and document executor registration and
stream-lifetime constraints.
- Updated dependencies
[[`2036247`](2036247),
[`de8d985`](de8d985),
[`306a40f`](306a40f),
[`515dc9f`](515dc9f),
[`878979b`](878979b),
[`3949c07`](3949c07),
[`5589df7`](5589df7)]:
  - @workflow/cli@5.0.3
  - @workflow/core@5.2.0
  - @workflow/next@5.0.3
  - @workflow/nitro@5.0.3
  - @workflow/typescript-plugin@5.0.0
  - @workflow/errors@5.0.3
  - @workflow/astro@5.0.3
  - @workflow/nest@5.1.1
  - @workflow/rollup@5.0.3
  - @workflow/sveltekit@5.0.3
  - @workflow/nuxt@5.0.3
## @workflow/world@5.0.3

### Patch Changes

- [#4712](#4712)
[`46b3bfb`](46b3bfb)
Thanks [@karthikscale3](https://github.com/karthikscale3)! - Reduce
redundant schema compilation by compiling final event schemas only and
reusing compiled v4 event-response and paginated response schemas.
## @workflow/world-postgres@5.0.3

### Patch Changes

- Updated dependencies
[[`46b3bfb`](46b3bfb),
[`0139561`](0139561),
[`b38360d`](b38360d)]:
  - @workflow/world@5.0.3
  - @workflow/world-local@5.1.0
  - @workflow/errors@5.0.3
## @workflow/world-testing@5.0.3

### Patch Changes

- Updated dependencies
[[`2036247`](2036247),
[`de8d985`](de8d985),
[`306a40f`](306a40f),
[`46b3bfb`](46b3bfb),
[`515dc9f`](515dc9f),
[`878979b`](878979b),
[`3949c07`](3949c07),
[`5589df7`](5589df7)]:
  - @workflow/cli@5.0.3
  - @workflow/core@5.2.0
  - @workflow/world@5.0.3
  - workflow@5.2.0

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
pranaygp pushed a commit that referenced this pull request Oct 8, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to stable, this PR
will be updated.


# Releases
## @workflow/astro@4.0.28

### Patch Changes

- Updated dependencies []:
  - @workflow/builders@4.1.18
  - @workflow/rollup@4.0.28
  - @workflow/vite@4.0.28
## @workflow/builders@4.1.18

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32)]:
  - @workflow/core@4.8.13
  - @workflow/errors@4.2.2
## @workflow/cli@4.3.17

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32),
[`fc18c67`](fc18c67),
[`73312eb`](73312eb)]:
  - @workflow/world@4.5.1
  - @workflow/core@4.8.13
  - @workflow/web@4.1.29
  - @workflow/world-local@4.4.3
  - @workflow/errors@4.2.2
  - @workflow/world-vercel@4.7.7
  - @workflow/builders@4.1.18
## @workflow/core@4.8.13

### Patch Changes

- [#3914](#3914)
[`5c25b32`](5c25b32)
Thanks [@shalabhc](https://github.com/shalabhc)! - Accept lazy completed
and failed runs whose payload is represented by a remote reference.

- Updated dependencies
[[`5c25b32`](5c25b32),
[`73312eb`](73312eb)]:
  - @workflow/world@4.5.1
  - @workflow/world-local@4.4.3
  - @workflow/errors@4.2.2
  - @workflow/world-vercel@4.7.7
## @workflow/nest@4.0.29

### Patch Changes

- Updated dependencies []:
  - @workflow/builders@4.1.18
## @workflow/next@4.1.17

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32)]:
  - @workflow/core@4.8.13
  - @workflow/builders@4.1.18
## @workflow/nitro@4.1.19

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32),
[`fc18c67`](fc18c67)]:
  - @workflow/core@4.8.13
  - @workflow/web@4.1.29
  - @workflow/builders@4.1.18
  - @workflow/rollup@4.0.28
  - @workflow/vite@4.0.28
## @workflow/nuxt@4.0.29

### Patch Changes

- Updated dependencies []:
  - @workflow/nitro@4.1.19
## @workflow/rollup@4.0.28

### Patch Changes

- Updated dependencies []:
  - @workflow/builders@4.1.18
## @workflow/sveltekit@4.0.28

### Patch Changes

- Updated dependencies []:
  - @workflow/builders@4.1.18
  - @workflow/rollup@4.0.28
  - @workflow/vite@4.0.28
## @workflow/vite@4.0.28

### Patch Changes

- Updated dependencies []:
  - @workflow/builders@4.1.18
## @workflow/vitest@4.0.29

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32),
[`73312eb`](73312eb)]:
  - @workflow/world@4.5.1
  - @workflow/core@4.8.13
  - @workflow/world-local@4.4.3
  - @workflow/builders@4.1.18
  - @workflow/rollup@4.0.28
## @workflow/web@4.1.29

### Patch Changes

- [#4736](#4736)
[`fc18c67`](fc18c67)
Thanks [@TooTallNate](https://github.com/TooTallNate)! - Upgrade
`react-router` and `@react-router/*` to 7.18.4 to address CVE-2026-34077
and GHSA-qwww-vcr4-c8h2
## @workflow/web-shared@4.1.29

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32)]:
  - @workflow/world@4.5.1
  - @workflow/core@4.8.13
## workflow@4.8.13

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32)]:
  - @workflow/core@4.8.13
  - @workflow/cli@4.3.17
  - @workflow/errors@4.2.2
  - @workflow/next@4.1.17
  - @workflow/nitro@4.1.19
  - @workflow/typescript-plugin@4.0.3
  - @workflow/astro@4.0.28
  - @workflow/nest@4.0.29
  - @workflow/rollup@4.0.28
  - @workflow/sveltekit@4.0.28
  - @workflow/nuxt@4.0.29
## @workflow/world@4.5.1

### Patch Changes

- [#3914](#3914)
[`5c25b32`](5c25b32)
Thanks [@shalabhc](https://github.com/shalabhc)! - Accept lazy completed
and failed runs whose payload is represented by a remote reference.
## @workflow/world-local@4.4.3

### Patch Changes

- [#4742](#4742)
[`73312eb`](73312eb)
Thanks [@pranaygp](https://github.com/pranaygp)! - Make a tagged
`clear()` delete only its own tag's lock files, so it no longer reopens
another tag's disposed hooks mid-run.

- Updated dependencies
[[`5c25b32`](5c25b32)]:
  - @workflow/world@4.5.1
  - @workflow/errors@4.2.2
## @workflow/world-postgres@4.3.10

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32),
[`73312eb`](73312eb)]:
  - @workflow/world@4.5.1
  - @workflow/world-local@4.4.3
  - @workflow/errors@4.2.2
## @workflow/world-testing@4.1.28

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32)]:
  - @workflow/world@4.5.1
  - @workflow/core@4.8.13
  - @workflow/cli@4.3.17
  - workflow@4.8.13
## @workflow/world-vercel@4.7.7

### Patch Changes

- Updated dependencies
[[`5c25b32`](5c25b32)]:
  - @workflow/world@4.5.1
  - @workflow/errors@4.2.2

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
pranaygp added a commit that referenced this pull request Oct 9, 2026
Catches the docs up with SDK changes that shipped in the last day
without (complete) docs updates.

| Change | Source PR | Docs edit |
| --- | --- | --- |
| Stream writes on the Vercel World use the WebSocket transport by
default (released in `@workflow/world-vercel` 5.2.0) | #4722 | The PR
updated the Worlds pages. The What's New WebSocket bullet now covers
stream writes and links `WORKFLOW_STREAMS_TRANSPORT` |
| world-local stores events and steps in one directory per run, and
deletes flat-layout data on upgrade (released in `@workflow/world-local`
5.1.0) | #4718 | The PR documented the new layout. Added the upgrade
behavior to "Data directory layout" on the Local World page: old data is
deleted on start, the CLI and web UI never delete it, and downgrading
needs a manual clear |
| `workflow inspect` tables show short workflow names, and `inspect -n`
accepts them | #4760 | The PR updated CLI and Web UI. Versioning's
cancel recipe now copies the full `workflowName` from `inspect runs
--json`, since `workflow cancel --workflowName` stays exact |

I reviewed the other PRs from the last day and they need no further
docs: the hook race fix (#4324), fan-out chunk cap (#4727), and
Performance page (#4782) shipped their own docs, the in-band writer
fence (#4733) is not used by the runtime yet, and the tagged `clear()`
fix (#4742), schema compilation (#4712), dependency bumps, release CI,
and world-sim changes aren't user-facing. The Workflows pages in
`vercel/front` were also checked and need no changes for these.

## Docs Preview

| Page | Preview |
| --- | --- |
| What's New | [Also new in
5.0](https://workflow-docs-git-workflow-docs-cleanup-fri-9-oct.vercel.sh/docs/whats-new#also-new-in-50)
|
| Local World | [Data directory
layout](https://workflow-docs-git-workflow-docs-cleanup-fri-9-oct.vercel.sh/worlds/local#data-directory-layout)
|
| Versioning | [Fixing in-flight
runs](https://workflow-docs-git-workflow-docs-cleanup-fri-9-oct.vercel.sh/docs/foundations/versioning#fixing-in-flight-runs)
|

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
Co-authored-by: Pranay Prakash <1797812+pranaygp@users.noreply.github.com>

This branch was successfully deployed

1 active deployment
Preview – workflow-docs — 9f543161 Deployed Oct 8, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants