Repository navigation
[world-local] Keep other tags' lock files in a tagged clear() - #4742
Conversation
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>
🦋 Changeset detectedLatest commit: 9f54316 The changes in this PR will be included in the next version bump. This PR includes changesets to release 19 packages
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 |
🧪 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.
E2E Test SummarySummary
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
✅ 🌐 Cross-language Conformance
✅ dynamic-runs
✅ vercel-http-transport
✅ vercel-multi-region
✅ vercel-ws-transport
|
📊 Workflow Benchmarkscommit Backend:
📈 STSO distribution vs main (inline / queue-hop histograms)1020 steps (inline) Cumulative STSO time: main 115030ms → this run 99503ms (Δ -15527ms, -13%) ℹ️ Metric definitions & methodologyThe 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). Best/P75/P90/P99 deltas compare against the most recent benchmark run on 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 = Cold starts stay in the numbers (real bursty-workload latency, inflates P75+); Best is the warm floor. |
Sim WorldSimulated world deterministic testing for races. Traces 🟠 world-sim scenario book — 1 fail of 42 total
Full trace: |
About these numbersSizes are gzip; parentheses show the change against
|
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>
|
Backport PR opened against |
… 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>
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>
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>
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>
Fixes the
Vitest Plugin Tests (node)failure onmain(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()ranrm -rf <dataDir>/.locks, deleting every tag's locks, not only its own. Hook dispose locks live there (.locks/hooks/<hookId>.disposed.<tag>).@workflow/vitestgives each worker its own tag on one shared data directory and callsclear()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 ofreuseHookTokenWorkflow:hook_disposedfor the previous round's hook writes its dispose lock and releases the token claim.hook_createdfor the new hook finds no claim and rebuilds it from the event log.hook_createdas 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.hook_conflictagainst 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 sharedevents/andsteps/, so the.locksdelete 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 itsclear()) 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 taggedclear()only deletes its own tag's files.Verification
tag.test.tscases: a taggedclear()keeps other tags' lock files, and doesn't turn another tag's dispose-then-recreate into ahook_conflict. Both fail onmain; the second fails with the samehook_conflict.hook-token-reuse.test.tswith a second tagged world callingclear()every 20ms fails 3/3 onmainwithround 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/vitestpasses on bothWORKFLOW_VM=nodeandquickjs.🤖 Generated with Claude Code