Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
202 commits
Select commit Hold shift + click to select a range
71e79bc
[core] Single-orchestrator runtime (spec 9)
VaguelySerious Oct 9, 2026
3a3a193
world-vercel: end the live feed on the backend's terminal notice
VaguelySerious Oct 13, 2026
42971f5
Merge origin/main into peter/single-orchestrator
VaguelySerious Oct 13, 2026
142dc76
[core] Format the QuickJS entrypoint
VaguelySerious Oct 13, 2026
3a199c9
Merge branch 'main' of https://github.com/vercel/workflow into peter/…
VaguelySerious Oct 13, 2026
55e9a1a
[core] Create inline steps by their first step_started alone on spec 9
VaguelySerious Oct 13, 2026
260909f
[docs] Document inline steps created by their first step_started
VaguelySerious Oct 13, 2026
6684099
Merge remote-tracking branch 'origin/main' into peter/single-orchestr…
VaguelySerious Oct 13, 2026
89c587c
[world-sim] Record spec-9 inline steps by their first step_started; e…
VaguelySerious Oct 14, 2026
f085b14
[world-testing] Accept inline step inputs on step_started in the reso…
VaguelySerious Oct 14, 2026
cacdf29
[world-postgres] Find an inline step's creation on its step_started i…
VaguelySerious Oct 14, 2026
89920ef
[core] Key QuickJS terminal-inert on log position, kept in the VM heap
VaguelySerious Oct 14, 2026
4f83dbf
[core] Cap orchestrator event batches at 16 events
VaguelySerious Oct 14, 2026
c0d750d
[core] Restore retained-VM coverage, drop step_started startReason
VaguelySerious Oct 14, 2026
534306e
Merge remote-tracking branch 'origin/main' into peter/single-orchestr…
VaguelySerious Oct 15, 2026
ec6ebd0
[core] Move the single-orchestrator (spec 9) modules into runtime/spec9
VaguelySerious Oct 15, 2026
1535692
[core] Run spec 8 and spec 9 orchestrators side by side, routed by ru…
VaguelySerious Oct 15, 2026
41bb00e
[core] Format the split orchestrator modules
VaguelySerious Oct 15, 2026
68cb3de
[core] Keep shared writers and wakes correct for spec 8 runs
VaguelySerious Oct 15, 2026
58d7dde
[core] Restore main's spec 8 orchestrator tests under runtime/spec8
VaguelySerious Oct 15, 2026
0c1d755
[core] Share identical spec 8/9 code; mint spec 8 under WORKFLOW_SPEC…
VaguelySerious Oct 15, 2026
7254fd5
[docs] Document spec 8 and spec 9 runs side by side
VaguelySerious Oct 15, 2026
504b28c
[world-testing] Finish a spec 8 run after its Local World moves to sp…
VaguelySerious Oct 15, 2026
ee28454
[world-vercel] Serialize spec 9 orchestrators with a VQS concurrency key
VaguelySerious Oct 19, 2026
8c6b923
Merge remote-tracking branch 'origin/main' into peter/single-orchestr…
VaguelySerious Oct 20, 2026
a29ce9b
[world-vercel] Pick up vercel/vqs#233's lease renewal on coalesced re…
VaguelySerious Oct 20, 2026
1e6ac7f
[core] Sort spec 8 suspension handler imports; [docs] Point mintedSpe…
VaguelySerious Oct 20, 2026
b19584c
[ci] Stage workbenches with URL-pinned catalog deps; [core] temporary…
VaguelySerious Oct 20, 2026
f14aca1
[world-vercel] Pick up vercel/vqs#233's key forwarding on single send…
VaguelySerious Oct 20, 2026
12894c5
[core] Measure a coalesced re-run's deadline from its invocation; [wo…
VaguelySerious Oct 20, 2026
16f7001
[docs] Add a temporary review page for the single-orchestrator change
VaguelySerious Oct 20, 2026
5d513ea
[docs] Review page: compare queue topology to main, record live hando…
VaguelySerious Oct 20, 2026
7e8ad1d
[docs] Review page: storm cascade analysis, wakeUp bugs, spec 8 redel…
VaguelySerious Oct 20, 2026
0fa41f1
[builders] Restore main's single flow queue trigger; [docs] restore W…
VaguelySerious Oct 20, 2026
789e103
[world][core] Fold handoff, re-run and report signals into the base c…
VaguelySerious Oct 20, 2026
27cfda6
[world-vercel] Restore main's queue topology and continuation for spe…
VaguelySerious Oct 20, 2026
e0c54bb
[docs] Document the spec-dependent { timeoutSeconds } contract, the r…
VaguelySerious Oct 20, 2026
94c7c14
Merge remote-tracking branch 'origin/main' into peter/single-orchestr…
VaguelySerious Oct 20, 2026
dfbb6df
[core] Keep QuickJS terminal-inert bookkeeping out of the long-lived …
VaguelySerious Oct 20, 2026
ecf4ec4
[core][world-testing] Fix three timing-dependent tests
VaguelySerious Oct 20, 2026
ad998a2
[core] Store in-band occurredAt in the remaining runtime test mocks
VaguelySerious Oct 20, 2026
bc75d58
[core][world][world-vercel] Drop replacement messages and unkeyed orc…
VaguelySerious Oct 20, 2026
dfff41b
[docs] Document fence refusal, in-place restarts and best-effort wake…
VaguelySerious Oct 20, 2026
b97ad32
[changeset] Note that run.wakeUp() is best-effort on Vercel while the…
VaguelySerious Oct 20, 2026
5ed5339
[core] Arm the next sleep's timer after an earlier concurrent sleep c…
VaguelySerious Oct 20, 2026
c00fcd5
[docs] Review page: QuickJS, run-ahead stop re-runs, heap fix, benchm…
VaguelySerious Oct 21, 2026
945ee60
[world-vercel] Pick up the rebuilt @vercel/queue preview tarball
VaguelySerious Oct 21, 2026
6d9bfb4
[world-vercel] Drop the backend preview URL override
VaguelySerious Oct 22, 2026
f5caa5f
Merge origin/main into peter/single-orchestrator
VaguelySerious Oct 22, 2026
6a3d1d2
[world-vercel] Pick up the rebuilt @vercel/queue preview tarball inte…
VaguelySerious Oct 22, 2026
97bd73d
[core] Retry the restart-recovery test's temp dir cleanup on Windows
VaguelySerious Oct 22, 2026
2b80f92
[core] Poll the step list in the step-error e2e tests
VaguelySerious Oct 22, 2026
a74038a
[core] Name the run's spec version on every spec 9 queue send
VaguelySerious Oct 22, 2026
1bf0acd
[core] Wake the run when the in-band fence refuses an orchestrator write
VaguelySerious Oct 22, 2026
365fab0
[core] Keep a sleep's timer chain alive when the queue coalesces a timer
VaguelySerious Oct 22, 2026
af94e2c
[world-vercel] Redeliver a non-finite spec 9 timeout instead of handi…
VaguelySerious Oct 22, 2026
3cda0ec
[docs] Update the single-orchestrator review for the fence wake and t…
VaguelySerious Oct 22, 2026
7c8a90b
[core] Drop unused runId destructures in the orchestrator tests
VaguelySerious Oct 22, 2026
ff26315
[world-vercel] Use the released @vercel/queue 0.9.0
VaguelySerious Oct 22, 2026
75ddd45
[core] Fail an exhausted attempt before running an inline step body
VaguelySerious Oct 23, 2026
f496b33
[docs] Add temporary single-orchestrator diagram pages
VaguelySerious Oct 23, 2026
6cbe1fc
[world-local] Wait for the 404 log in the late route registration test
VaguelySerious Oct 23, 2026
3c7440e
[core] Commit spec 9 fan-out creations in fewer batches (#5030)
VaguelySerious Oct 23, 2026
2cd65d5
[core] Don't ask spec 9 write batches for resolved payloads (#5034)
VaguelySerious Oct 23, 2026
d617404
[workbench] Pin the nest workbench to Node 24
VaguelySerious Oct 23, 2026
83ee220
[core] Start a background step's body while its step_started is in fl…
VaguelySerious Oct 23, 2026
e277334
[core] Dispatch a spec 9 fan-out's background steps in parallel queue…
VaguelySerious Oct 23, 2026
5b53481
[core] Share the first creation batch with inline starts, and queue i…
VaguelySerious Oct 23, 2026
072fff9
[docs] Describe the spec 9 fan-out creation and dispatch changes
VaguelySerious Oct 23, 2026
c9446d4
[core] Wake the spec 9 orchestrator without reading the log after a b…
VaguelySerious Oct 23, 2026
76643fe
[core] Re-send the wake when a redelivered step message finds its ste…
VaguelySerious Oct 23, 2026
0f58c8d
[docs] Bring the single-orchestrator review and diagram pages up to date
VaguelySerious Oct 23, 2026
f1d403f
[docs] Record that CI is green with the nest workbench pin
VaguelySerious Oct 23, 2026
670ea7b
[core] Start a spec 9 fan-out's inline step bodies while its backgrou…
VaguelySerious Oct 23, 2026
560e89d
[docs] Update the single-orchestrator review's fan-out latency with t…
VaguelySerious Oct 23, 2026
9f96925
[core] Run one idle poll per suspension generation
VaguelySerious Oct 23, 2026
ff5ef5c
[core] Return from disabled debug logging before building anything
VaguelySerious Oct 23, 2026
ad9f92f
[core] Offer an event only to the consumers that can take it
VaguelySerious Oct 23, 2026
48c5865
[core] Back off the orchestrator's log-tail poll while the live feed …
VaguelySerious Oct 23, 2026
589ad95
[core] Add a changeset for the fan-out join fixes
VaguelySerious Oct 23, 2026
d5d8977
[docs] Record the cause and fixes of the 1024-branch join regression
VaguelySerious Oct 23, 2026
e6c4252
[core] Bound the single orchestrator's incremental event log reads (#…
VaguelySerious Oct 23, 2026
8a79811
[core] Enqueue each later creation batch's steps once that batch commits
VaguelySerious Oct 23, 2026
5724bf8
[docs] Record per-batch dispatch of a fan-out's later creation batches
VaguelySerious Oct 23, 2026
cea1893
Merge origin/main into peter/single-orchestrator
VaguelySerious Oct 23, 2026
d0437df
[docs] Elaborate consume after commit with a worked example
VaguelySerious Oct 23, 2026
c3cfbcd
[core] Poll the log tail every 10s while the live feed is up
VaguelySerious Oct 23, 2026
eec0d6d
Merge origin/main into peter/single-orchestrator
VaguelySerious Oct 23, 2026
c215273
[changeset] Condense the single-orchestrator changesets
VaguelySerious Oct 23, 2026
28018f6
[core] Stop the QuickJS live feed when the spec 9 loop throws
VaguelySerious Oct 26, 2026
86eb382
[core] Record why a spec 9 delivery ended early on its span
VaguelySerious Oct 26, 2026
baeef30
[core] Record the routed spec version and queue counts on workflow.ex…
VaguelySerious Oct 26, 2026
722fe2a
[core] Cancel spec 8 runs without the out-of-band wake
VaguelySerious Oct 26, 2026
d0fe48e
[core] Forget a wake's pending-wait marks when its publish fails
VaguelySerious Oct 26, 2026
8820365
[docs] Say that an open hook alone keeps a spec 9 orchestrator lingering
VaguelySerious Oct 26, 2026
594cc5f
[docs] Say that a spec 9 wakeUp() can be dropped during the linger
VaguelySerious Oct 26, 2026
9cd2281
[docs] List the spec 9 known limitations on the permanent page
VaguelySerious Oct 26, 2026
aea0fd2
[core] Redeliver a max-deliveries failure the in-band fence refuses
VaguelySerious Oct 26, 2026
8e6e547
[world] Keep creatorMessageId on step_retrying
VaguelySerious Oct 26, 2026
b075bd0
[core] Scope the inline-retry re-send to the message that moved it
VaguelySerious Oct 26, 2026
90dde6e
[docs] Qualify maxRetries: 0 as at most once per live owner
VaguelySerious Oct 26, 2026
a26b9a4
[core] Warn when a run's orchestrator writes keep being refused by th…
VaguelySerious Oct 26, 2026
5cd8631
[core] Keep an inline step retried on its own message out of the orch…
VaguelySerious Oct 26, 2026
d239120
[core] Count an inline step retried on its own message as another wri…
VaguelySerious Oct 26, 2026
6d1c305
[core] Keep speculative creation batches and run-ahead groups within …
VaguelySerious Oct 26, 2026
0d54ad6
[core] Fail an inline step whose retry would outlive its queue message
VaguelySerious Oct 26, 2026
cf8a20d
[core] Give a step message re-routed to its pinned deployment a step …
VaguelySerious Oct 26, 2026
8156506
[core] Record a background step delivery's decision on its span
VaguelySerious Oct 26, 2026
7f1bb96
Take peter/single-orchestrator's side of the two files main conflicts on
VaguelySerious Oct 26, 2026
b489e75
[core] Compute an in-band write's log position once
VaguelySerious Oct 26, 2026
d08a880
Merge origin/main into peter/single-orchestrator
VaguelySerious Oct 26, 2026
bfb970a
[core] Forward the run public key on a resilient run_started in both …
VaguelySerious Oct 26, 2026
2b02860
[core] Log a run-ahead hazard stop as a possible classification gap
VaguelySerious Oct 26, 2026
ddc7189
[core] Count a speculative fan-out's steps against WORKFLOW_RUN_AHEAD…
VaguelySerious Oct 26, 2026
5fe3e38
[core] Order a full load's fence snapshot with the in-band writer's q…
VaguelySerious Oct 26, 2026
b154d33
[world] Restore WORKFLOW_SEALED_LOG=0 and validate WORKFLOW_SPEC_VERSION
VaguelySerious Oct 26, 2026
dd5037f
[world] Stamp restart-recovery messages with the run's spec version
VaguelySerious Oct 26, 2026
d36a017
[core] Route a run by its own version over a wrong routing hint
VaguelySerious Oct 26, 2026
c349183
[world-vercel] Attest the spec 8 orchestrator's version on a spec 8 r…
VaguelySerious Oct 26, 2026
1f7b912
[world-postgres] Keep attempt across a spec 9 handoff
VaguelySerious Oct 26, 2026
87c4c9f
[docs] Correct the removed settings in runtime tuning
VaguelySerious Oct 26, 2026
e17bd0c
[world-sim] Gate only single-orchestrator messages, as the real World…
VaguelySerious Oct 26, 2026
4ed4f07
[world-postgres] Document the 4-hour named-queue lock after a hard kill
VaguelySerious Oct 26, 2026
80972c8
[world-vercel] Map only an entity 404 on a hook write to HookNotFound…
VaguelySerious Oct 26, 2026
53cd1e3
[world-sim] Key the fenced scenarios on events spec 9 runs write
VaguelySerious Oct 26, 2026
6292be4
[core] Linger only while the live event feed is live
VaguelySerious Oct 26, 2026
72954bb
[core] Wait for a background step's start before its body by default
VaguelySerious Oct 26, 2026
261dd84
[docs] Document the planned spec 9 opt-in switch
VaguelySerious Oct 26, 2026
26a8d61
[docs] Explain the post-pass linger an open hook causes
VaguelySerious Oct 26, 2026
5c0dbf3
[docs] Record that spec 9 wakeUp() stays best-effort for this release
VaguelySerious Oct 26, 2026
ea01262
[docs] List the single-orchestrator follow-ups to file after merge
VaguelySerious Oct 26, 2026
a09d313
[core] Batch a QuickJS spec 9 pass's step and wait creations
VaguelySerious Oct 26, 2026
2cfc12f
[docs] Record batched QuickJS spec 9 creations in the fan-out latency…
VaguelySerious Oct 26, 2026
8b6ef71
Hold back main's pre-split runtime edits for the #4661 merge
VaguelySerious Oct 26, 2026
62be2c9
[core] Apply main's abort framing and single-flight fix to the spec 8…
VaguelySerious Oct 26, 2026
931539b
Hold back main's edits to files the #4661 branch moved
VaguelySerious Oct 26, 2026
6d7e0f4
[core] Apply main's step-executor framing and single-flight tests to …
VaguelySerious Oct 26, 2026
2270ab0
Merge origin/main into peter/single-orchestrator
VaguelySerious Oct 26, 2026
3ba56bb
[core] Send a wait chain's next timer before committing wait_completed
VaguelySerious Oct 26, 2026
66b355d
[core] Arm every near-due wait when a spec 9 delivery suspends
VaguelySerious Oct 26, 2026
aa928b7
[world-vercel] Continue a spec 9 run on a new message near its expiry
VaguelySerious Oct 26, 2026
51a21f6
[core] Fail a spec 9 step whose message keeps failing past the delive…
VaguelySerious Oct 26, 2026
ea2aa05
[docs] Narrow the spec 9 liveness follow-up to the fence and re-send …
VaguelySerious Oct 26, 2026
427ded4
[world-vercel] Send a background step's creation time on its first start
VaguelySerious Oct 27, 2026
0528815
[core] Carry a spec 9 step's creation time to its first start
VaguelySerious Oct 27, 2026
1ddbe74
[core] Re-send unstarted spec 9 background steps from any non-first d…
VaguelySerious Oct 27, 2026
6b4c0ce
[core] End a fence-refused spec 9 delivery in a redelivery of its mes…
VaguelySerious Oct 27, 2026
1a808df
[world-local] Resend a spec 9 run's lost step messages and timers aft…
VaguelySerious Oct 27, 2026
4b94003
[docs] Record the spec 9 liveness decisions and the re-send rule
VaguelySerious Oct 27, 2026
bb70daf
[docs] Describe the fence refusal's redelivery in the World guide and…
VaguelySerious Oct 27, 2026
4b7610f
[docs] Correct the spec 9 pages against the re-send and refusal changes
VaguelySerious Oct 27, 2026
a45cfb0
[core] Wait for the recovered sleep's run_completed in the world-loca…
VaguelySerious Oct 27, 2026
1a02e52
[world-vercel] Continue a rotated spec 9 message on a continuation pa…
VaguelySerious Oct 27, 2026
ab37131
[world-postgres] Deduplicate a spec 9 run's keyed messages against an…
VaguelySerious Oct 27, 2026
dcddeb7
[core] Re-arm near-due spec 9 waits only from a delivery that may hol…
VaguelySerious Oct 27, 2026
030f774
[core] Derive the spec 9 re-send age bound from a World dedupe window…
VaguelySerious Oct 27, 2026
502f40d
[docs] Record the rotation, near-due re-arm and world-postgres dedupe…
VaguelySerious Oct 27, 2026
76f33f7
Hold back main's conflicting world-postgres edits for the #4661 merge
VaguelySerious Oct 28, 2026
281f150
[core] Stamp the spec 8 orchestrator's own sends with the run's spec …
VaguelySerious Oct 28, 2026
b48f83d
Merge origin/main into peter/single-orchestrator
VaguelySerious Oct 28, 2026
dbb4d8a
[world-postgres] Reconcile the job-lock lease docs with spec 9's per-…
VaguelySerious Oct 28, 2026
c88100f
[core] Drop the spec 9 QuickJS orchestrator's unused step-ownership b…
VaguelySerious Oct 28, 2026
157d8ce
[core] Narrow the spec 9 suspension handler's result to what its call…
VaguelySerious Oct 28, 2026
339c9a9
[core] Remove the per-process run spec cache
VaguelySerious Oct 28, 2026
0af8145
[docs] Describe spec routing without the run spec cache
VaguelySerious Oct 28, 2026
6a5d1f0
[core] Build and send spec 9 step messages in one place for both engines
VaguelySerious Oct 28, 2026
f4f8149
[core] Give the fake World its own batch limits and a finite key dedu…
VaguelySerious Oct 28, 2026
c9c1c27
[core] Test world-vercel's queue adapter against the real spec 9 runt…
VaguelySerious Oct 28, 2026
9856839
[docs] Record the 2026-10-28 review's changes and follow-ups
VaguelySerious Oct 28, 2026
6c6955d
[world-vercel] Rotate a spec 9 message on its last coalesced re-run too
VaguelySerious Oct 28, 2026
700c39d
[docs] Say when world-postgres should default the stale lock window o…
VaguelySerious Oct 28, 2026
e36eb29
[world-vercel] Resolve a spec 8 step_started response
VaguelySerious Oct 28, 2026
dfe9702
[core] Key replay payloads on the event's entity, not its position alone
VaguelySerious Oct 28, 2026
e3018c0
[core] Send a new wait's timer before its creation commits, and key e…
VaguelySerious Oct 28, 2026
af8f076
[core] Send a chain's next timer before a timer delivery completes it…
VaguelySerious Oct 28, 2026
008530f
[core] Re-send an inline step's background retry from any non-first d…
VaguelySerious Oct 28, 2026
7fc8954
[core] Mark a re-sent step message so that its first delivery reads t…
VaguelySerious Oct 28, 2026
f0849c9
[core] Stamp the spec 8 QuickJS hook-conflict wake and observe each c…
VaguelySerious Oct 28, 2026
3e3cd8c
[world-local] Keep retrying a failing message past a step message's s…
VaguelySerious Oct 28, 2026
15b1f0a
[world-vercel] Warn when the queue client offers no handoff outcomes …
VaguelySerious Oct 28, 2026
838cc29
[docs] Document the creation pre-send, keyed timer hops, the inline r…
VaguelySerious Oct 28, 2026
8c50d00
[world] Mint spec 8 by default and make spec 9 opt-in
VaguelySerious Oct 29, 2026
a2ddc02
[world-vercel] Route each run's events by its spec version
VaguelySerious Oct 29, 2026
f089295
[world-vercel] Send batches of runs below spec 9 over HTTP
VaguelySerious Oct 29, 2026
6a1353e
[world-testing] Skip the fence conformance for a World without the fence
VaguelySerious Oct 29, 2026
f2fd403
[core] Load the spec 9 orchestrator on the first spec 9 delivery
VaguelySerious Oct 29, 2026
0bf649f
[core] Point spec 9 tests at spec 9 now that spec 8 is the default
VaguelySerious Oct 29, 2026
70e4b37
[changeset] Describe spec 9 as opt-in, with upgrade notes
VaguelySerious Oct 29, 2026
4754c9c
[docs] Describe spec 9 as opt-in and restore the spec 8 defaults
VaguelySerious Oct 29, 2026
cd71167
Merge origin/main into peter/single-orchestrator
VaguelySerious Oct 29, 2026
cc518ab
[ci] Add a required spec 9 e2e lane on Vercel
VaguelySerious Oct 29, 2026
0f21b75
[docs] Record the spec 9 e2e lane as landed on the review page
VaguelySerious Oct 29, 2026
38f7e29
[docs] Document the spec 9 live event feed
VaguelySerious Oct 29, 2026
4de3495
[core] Bound a spec 9 delivery whose orchestrator fails to load
VaguelySerious Oct 29, 2026
ed5d0f1
[world] Scope the in-band fence JSDoc to Worlds that create spec 9 runs
VaguelySerious Oct 29, 2026
8d999e4
[ci] Race spec 9 runs in the Event Log Race Repro
VaguelySerious Oct 29, 2026
53fc8cc
[docs] Correct the spec 9 rollback floor and runtime-tuning details
VaguelySerious Oct 29, 2026
0915559
[docs] Consolidate the spec 9 pages into /docs/changelog/spec-9
VaguelySerious Oct 29, 2026
0239c72
[docs] Point the remaining spec 9 links at /docs/changelog/spec-9
VaguelySerious Oct 29, 2026
e6b64a7
[changeset] Drop the run-ahead, read-deadline and fan-out changesets
VaguelySerious Oct 29, 2026
fc0aa31
[changeset] Bump @workflow/vitest with the single-orchestrator changes
VaguelySerious Oct 29, 2026
6f79f60
[docs] Keep spec 9 on its changelog page, spec 8 docs as on main
VaguelySerious Oct 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/quickjs-inert-after-terminal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workflow/core": patch
---

With `WORKFLOW_VM=quickjs`, replay ignores events for a step, wait or hook after its terminal event, as the default engine does.
16 changes: 16 additions & 0 deletions .changeset/single-orchestrator-runtime.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@workflow/core": minor
"@workflow/world": minor
"@workflow/errors": patch
"@workflow/world-vercel": minor
"@workflow/world-local": minor
"@workflow/world-postgres": minor
"@workflow/world-testing": patch
"@workflow/vitest": patch
---

Add opt-in spec 9 runs, driven by a single orchestrator per run: set `WORKFLOW_SPEC_VERSION=9`. New runs stay on spec 8 by default.

- Without the setting, new runs are spec 8 and behave as before.
- To opt in, first upgrade every deployment and process that calls `start()` or shares the World to this release, then set `WORKFLOW_SPEC_VERSION=9` on all of them. An older release fails writes to spec 9 runs (reads still work, nothing is corrupted), so do not roll back below this release while spec 9 runs are in flight.
- Custom Worlds: `mintedSpecVersion()` keeps creating spec 8 runs. A World that implements the in-band writer fence declares `capabilities.inBandFence` and passes `{ inBandFence: true }` to create spec 9 runs.
5 changes: 5 additions & 0 deletions .changeset/world-local-queue-slot-per-delivery.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-local": patch
---

Hold a queue concurrency slot for each delivery, not across the sleeps and backoffs between deliveries.
5 changes: 5 additions & 0 deletions .changeset/world-local-retry-backoff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workflow/world-local": patch
---

Back off retries of a queue message whose deliveries keep failing after 32 attempts, as Vercel Queues does.
11 changes: 11 additions & 0 deletions .github/scripts/vercel-e2e-matrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ const HTTP_TRANSPORT_APPS = [
'hono',
];

// Apps whose dedicated deployment opts in to spec 9 runs
// (WORKFLOW_SPEC_VERSION=9). Every other Vercel lane runs the default, spec 8.
// Both VMs, since each engine has its own spec 9 orchestrator.
const SPEC9_APPS = ['nextjs-turbopack'];

function project(name) {
const found = PROJECTS.find((candidate) => candidate.name === name);
if (!found) {
Expand Down Expand Up @@ -140,6 +145,12 @@ function buildMatrices() {
app: appEntry(project(name)),
})),
},
'vercel-spec9-matrix': {
include: SPEC9_APPS.flatMap((name) => {
const app = project(name);
return (app.vms ?? VMS).map((vm) => ({ app: appEntry(app), vm }));
}),
},
};
}

Expand Down
7 changes: 7 additions & 0 deletions .github/scripts/vercel-e2e-matrix.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ test('the transport lanes run their fixed app subsets', () => {
]);
});

test('the spec 9 lane runs its app on both VMs', () => {
assert.deepEqual(names(matrices['vercel-spec9-matrix']), [
'nextjs-turbopack/node',
'nextjs-turbopack/quickjs',
]);
});

test('matrix entries carry no lane-internal fields', () => {
for (const { app } of matrices['vercel-prod-matrix'].include) {
assert.deepEqual(Object.keys(app).sort(), [
Expand Down
61 changes: 55 additions & 6 deletions .github/workflows/event-log-race-repro.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,15 @@ concurrency:
cancel-in-progress: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/stable' }}

jobs:
# Two cells on Vercel. `default` races the standard Git preview, whose new
# runs take the default spec version (8). `spec9` races a dedicated preview
# deployed with WORKFLOW_SPEC_VERSION=9, the single orchestrator, which is
# opt-in and so has no other race coverage. The variable is set on that
# deployment AND on the runner: a cross-deployment start() stamps the lower
# of the two versions, and the harness fails if a run comes back at another
# spec version. Both cells gate.
event-log-race-repro:
name: Event Log Race Repro
name: Event Log Race Repro${{ matrix.spec == 'spec9' && ' (spec 9)' || '' }}
runs-on: ubuntu-latest
# Sized for the harness' default scale: its own test timeout is
# `budget_ms + run_timeout_ms + 60s` (~17 min at the defaults), and the rest is
Expand All @@ -110,6 +117,10 @@ jobs:
# the job before the summary is written.
timeout-minutes: 25
if: ${{ github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'event-log-race-repro') }}
strategy:
fail-fast: false
matrix:
spec: [default, spec9]
# No comment-writing scopes: the PR comment for every lane is written by
# `event-log-race-repro-comment` below.
permissions:
Expand All @@ -135,12 +146,48 @@ jobs:
run: pnpm turbo run build --filter='@workflow/cli'

- name: Resolve Vercel deployment
id: deployment
id: git-deployment
if: ${{ matrix.spec == 'default' }}
uses: ./.github/actions/resolve-vercel-deployment
with:
project-id: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
token: ${{ secrets.VERCEL_LABS_TOKEN }}

# Same dedicated-deployment pattern as `e2e-vercel-spec9` in tests.yml.
- name: Deploy a dedicated spec 9 preview
id: spec9-deployment
if: ${{ matrix.spec == 'spec9' }}
uses: ./.github/actions/deploy-vercel-preview
with:
project-id: "prj_yjkM7UdHliv8bfxZ1sMJQf1pMpdi"
team-id: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
token: ${{ secrets.VERCEL_LABS_TOKEN }}
env: |
WORKFLOW_SPEC_VERSION=9

- name: Select deployment
id: deployment
env:
GIT_URL: ${{ steps.git-deployment.outputs.deployment-url }}
GIT_ID: ${{ steps.git-deployment.outputs.deployment-id }}
GIT_ENVIRONMENT: ${{ steps.git-deployment.outputs.environment }}
SPEC9_URL: ${{ steps.spec9-deployment.outputs.deployment-url }}
SPEC9_ID: ${{ steps.spec9-deployment.outputs.deployment-id }}
run: |
if [ "${{ matrix.spec }}" = spec9 ]; then
{
echo "deployment-url=$SPEC9_URL"
echo "deployment-id=$SPEC9_ID"
echo "environment=preview"
} >> "$GITHUB_OUTPUT"
else
{
echo "deployment-url=$GIT_URL"
echo "deployment-id=$GIT_ID"
echo "environment=$GIT_ENVIRONMENT"
} >> "$GITHUB_OUTPUT"
fi

- name: Run event log race repro
id: repro
continue-on-error: true
Expand All @@ -150,6 +197,7 @@ jobs:
DEPLOYMENT_URL: ${{ steps.deployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.deployment.outputs.deployment-id }}
APP_NAME: nextjs-turbopack
WORKFLOW_SPEC_VERSION: ${{ matrix.spec == 'spec9' && '9' || '' }}
WORKFLOW_VERCEL_ENV: ${{ steps.deployment.outputs.environment }}
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: "team_nO2mCG4W8IxPIeKoSsqwAxxB"
Expand Down Expand Up @@ -193,7 +241,7 @@ jobs:
run: |
node .github/scripts/render-event-log-race-repro-results.js \
event-log-race-repro-results.json \
--label vercel \
--label "${{ matrix.spec == 'spec9' && 'vercel-spec9' || 'vercel' }}" \
--run-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
--run-attempt "${{ github.run_attempt }}" \
--timestamp "$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
Expand All @@ -203,7 +251,7 @@ jobs:
if: always()
uses: actions/upload-artifact@v4
with:
name: event-log-race-repro-results
name: event-log-race-repro-results${{ matrix.spec == 'spec9' && '-vercel-spec9' || '' }}
path: |
event-log-race-repro-results.json
event-log-race-repro-summary.md
Expand Down Expand Up @@ -383,10 +431,11 @@ jobs:
previous_comment_args=(--previous-comment event-log-race-repro-previous-comment.md)
fi

# Lane order here is the row order in the comment: the gating lane
# first, then the two report-only worlds.
# Lane order here is the row order in the comment: the two gating
# Vercel lanes first, then the two report-only worlds.
node .github/scripts/render-event-log-race-repro-results.js \
--lane vercel=lane-results/event-log-race-repro-results/event-log-race-repro-results.json \
--lane vercel-spec9=lane-results/event-log-race-repro-results-vercel-spec9/event-log-race-repro-results.json \
--lane local=lane-results/event-log-race-repro-results-world-local/event-log-race-repro-results.json \
--lane postgres=lane-results/event-log-race-repro-results-world-postgres/event-log-race-repro-results.json \
--run-url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
Expand Down
98 changes: 96 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
vercel-prod-matrix: ${{ steps.vercel-matrix.outputs.vercel-prod-matrix }}
vercel-ws-transport-matrix: ${{ steps.vercel-matrix.outputs.vercel-ws-transport-matrix }}
vercel-http-transport-matrix: ${{ steps.vercel-matrix.outputs.vercel-http-transport-matrix }}
vercel-spec9-matrix: ${{ steps.vercel-matrix.outputs.vercel-spec9-matrix }}
steps:
- name: Checkout Repo
uses: actions/checkout@v4
Expand Down Expand Up @@ -649,6 +650,97 @@ jobs:
token: ${{ secrets.VERCEL_LABS_TOKEN }}
e2e-start-ms: ${{ steps.e2eStart.outputs.ms }}

e2e-vercel-spec9:

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.

[minor, coverage] With spec 8 now the minted default, this lane is the only e2e coverage of spec 9. The Event Log Race Repro (event-log-race-repro.yml) resolves the standard preview and sets no WORKFLOW_SPEC_VERSION on the deployment or the runner, so from this head on it races spec 8 runs only. That's the harness that caught the spec 9 run-ahead CORRUPTED_EVENT_LOG fixed in dfe9702; its last spec 9 passes were at dfe9702 and 838cc29, two clean runs of an intermittent failure. The local-World e2e lanes (e2e-local-dev/prod/postgres) also no longer run spec 9 on world-local or world-postgres.

Suggestion: give the race repro a spec 9 variant (a preview with WORKFLOW_SPEC_VERSION=9, with the variable also in the runner env as in this lane), and run at least one local-World lane with WORKFLOW_SPEC_VERSION=9. Otherwise a regression in the spec 9 race paths only shows up in unit tests.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

(AI) Added in 8d999e4. The Vercel lane of the Event Log Race Repro is now a matrix with a spec9 cell, which deploys a dedicated preview with WORKFLOW_SPEC_VERSION=9 and sets the variable on the runner too. The harness records each run's spec version and fails if a run comes back at another one; the cell gates like the default one and has its own row in the PR comment. First run, at 53fc8cc: 32 of 32 runs completed, all 32 spec 9, 0 corrupted. The local-World lanes still run spec 8 only.

# Spec 9 runs (the single orchestrator) are opt-in with
# WORKFLOW_SPEC_VERSION=9, so every other Vercel lane runs the default,
# spec 8. This lane deploys its own preview with the variable set and runs
# the full suite against it on both VMs.
#
# The variable is set on the deployment AND on the test runner: a
# cross-deployment start() stamps the lower of the target's and the
# caller's version, so with only the deployment env set this lane would
# silently test spec 8. The e2e test "stamps the spec version the lane
# opts in to" fails if either side stops seeing it.
#
# Same dedicated-deployment pattern as e2e-vercel-ws-transport above.
name: E2E Vercel Spec 9 Test (${{ matrix.app.name }}, ${{ matrix.vm }})
runs-on: ubuntu-latest
timeout-minutes: 30
needs: ci-scope
if: ${{ needs.ci-scope.outputs.fast-path != 'true' }}
concurrency:
group: e2e-vercel-spec9-${{ matrix.app.name }}-${{ matrix.vm }}-${{ needs.ci-scope.outputs.e2e-vercel-slot }}
queue: max
permissions:
id-token: write
contents: read
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.ci-scope.outputs.vercel-spec9-matrix) }}
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
WORKFLOW_PUBLIC_MANIFEST: '1'
VERCEL_TEAM_ID: ${{ needs.ci-scope.outputs.vercel-team-id }}
steps:
- name: Checkout Repo
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Setup environment
uses: ./.github/actions/setup-workflow-dev
with:
build-packages: 'false'

- name: Build CLI
run: pnpm turbo run build --filter='@workflow/cli'

- name: Deploy a dedicated spec 9 preview
id: deployment
uses: ./.github/actions/deploy-vercel-preview
with:
project-id: ${{ matrix.app.project-id }}
team-id: ${{ env.VERCEL_TEAM_ID }}
token: ${{ secrets.VERCEL_LABS_TOKEN }}
env: |
WORKFLOW_SPEC_VERSION=9

- name: Record E2E start time
id: e2eStart
run: echo "ms=$(($(date +%s) * 1000))" >> "$GITHUB_OUTPUT"

- name: Run E2E Tests
run: pnpm run test:e2e --reporter=verbose --reporter=json --reporter=./packages/core/e2e/github-reporter.ts "--outputFile=e2e-vercel-spec9-$APP_NAME-$WORKFLOW_VM.json"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Keep explicit spec-9 storm coverage after the opt-in flip. This lane correctly tests spec 9, but test:e2e does not include event-log-race-repro.test.ts. The separate event-log-race-repro.yml sets no WORKFLOW_SPEC_VERSION=9, so its runner now starts default/spec-8 runs—even against an opted-in target, because cross-deployment start() caps at the caller's version. Its green checks therefore don't establish spec-9 stress coverage.

Please run the storm harness with both deployment and runner opted into 9, and assert the created run's spec, while retaining default/spec-8 coverage. Source/CI inspection only; no local tests run.


Local agent review (`openai/gpt-6-astra`)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

nah

env:
NODE_OPTIONS: "--enable-source-maps"
DEPLOYMENT_URL: ${{ steps.deployment.outputs.deployment-url }}
VERCEL_DEPLOYMENT_ID: ${{ steps.deployment.outputs.deployment-id }}
APP_NAME: ${{ matrix.app.name }}
WORKFLOW_VM: ${{ matrix.vm }}
WORKFLOW_SPEC_VERSION: '9'
WORKFLOW_VERCEL_ENV: "preview"
WORKFLOW_VERCEL_AUTH_TOKEN: ${{ secrets.VERCEL_LABS_TOKEN }}
WORKFLOW_VERCEL_TEAM: ${{ env.VERCEL_TEAM_ID }}
WORKFLOW_VERCEL_PROJECT: ${{ matrix.app.project-id }}
WORKFLOW_VERCEL_PROJECT_SLUG: ${{ matrix.app.project-slug }}
VERCEL_WORKFLOW_SERVER_URL: ${{ github.ref != 'refs/heads/main' && secrets.VERCEL_WORKFLOW_SERVER_URL || '' }}

- name: Report E2E results
if: always()
uses: ./.github/actions/report-vercel-e2e
with:
results-name: vercel-spec9-${{ matrix.app.name }}-${{ matrix.vm }}
app: ${{ matrix.app.name }}
summary-title: E2E Vercel Spec 9 (${{ matrix.app.name }}, ${{ matrix.vm }})
capture-runtime-logs: ${{ job.status == 'failure' }}
deployment-id: ${{ steps.deployment.outputs.deployment-id }}
environment: preview
project-id: ${{ matrix.app.project-id }}
team-id: ${{ env.VERCEL_TEAM_ID }}
token: ${{ secrets.VERCEL_LABS_TOKEN }}
e2e-start-ms: ${{ steps.e2eStart.outputs.ms }}

e2e-vercel-http-transport:
# The counterpart to e2e-vercel-ws-transport. With
# WORKFLOW_EVENTS_TRANSPORT defaulting to `ws`, e2e-vercel-prod — which
Expand Down Expand Up @@ -1461,7 +1553,7 @@ jobs:
summary:
name: E2E Summary
runs-on: ubuntu-latest
needs: [ci-scope, e2e-vercel-prod, e2e-vercel-multi-region, e2e-vercel-ws-transport, e2e-vercel-http-transport, e2e-local-dev, e2e-local-prod, e2e-local-postgres, e2e-python, e2e-windows]
needs: [ci-scope, e2e-vercel-prod, e2e-vercel-multi-region, e2e-vercel-ws-transport, e2e-vercel-http-transport, e2e-vercel-spec9, e2e-local-dev, e2e-local-prod, e2e-local-postgres, e2e-python, e2e-windows]
if: always() && !cancelled() && needs.ci-scope.outputs.fast-path != 'true'
timeout-minutes: 10

Expand Down Expand Up @@ -1497,7 +1589,7 @@ jobs:
e2e-required-check:
name: E2E Required Check
runs-on: ubuntu-latest
needs: [ci-scope, unit, e2e-package-build, e2e-vercel-prod, e2e-vercel-ws-transport, e2e-vercel-http-transport, e2e-local-dev, e2e-local-prod, e2e-local-postgres, e2e-windows]
needs: [ci-scope, unit, e2e-package-build, e2e-vercel-prod, e2e-vercel-ws-transport, e2e-vercel-http-transport, e2e-vercel-spec9, e2e-local-dev, e2e-local-prod, e2e-local-postgres, e2e-windows]
if: always()
timeout-minutes: 5

Expand All @@ -1509,6 +1601,7 @@ jobs:
VERCEL_STATUS: ${{ needs.e2e-vercel-prod.result }}
VERCEL_WS_STATUS: ${{ needs.e2e-vercel-ws-transport.result }}
VERCEL_HTTP_STATUS: ${{ needs.e2e-vercel-http-transport.result }}
VERCEL_SPEC9_STATUS: ${{ needs.e2e-vercel-spec9.result }}
LOCAL_DEV_STATUS: ${{ needs.e2e-local-dev.result }}
LOCAL_PROD_STATUS: ${{ needs.e2e-local-prod.result }}
POSTGRES_STATUS: ${{ needs.e2e-local-postgres.result }}
Expand Down Expand Up @@ -1554,6 +1647,7 @@ jobs:
[[ "$VERCEL_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-vercel-prod ($VERCEL_STATUS)")
[[ "$VERCEL_WS_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-vercel-ws-transport ($VERCEL_WS_STATUS)")
[[ "$VERCEL_HTTP_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-vercel-http-transport ($VERCEL_HTTP_STATUS)")
[[ "$VERCEL_SPEC9_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-vercel-spec9 ($VERCEL_SPEC9_STATUS)")
[[ "$LOCAL_DEV_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-local-dev ($LOCAL_DEV_STATUS)")
[[ "$LOCAL_PROD_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-local-prod ($LOCAL_PROD_STATUS)")
[[ "$POSTGRES_STATUS" == "success" ]] || FAILED_JOBS+=("e2e-local-postgres ($POSTGRES_STATUS)")
Expand Down
4 changes: 4 additions & 0 deletions docs/content/docs/v5/api-reference/workflow-api/get-run.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ export async function POST(req: Request) {

The options object is optional: `await run.cancel()` cancels the run without recording a reason.

## Spec 9 (experimental)

On runs of the experimental spec 9 runtime, `wakeUp()` asks the run's orchestrator to complete the sleeps instead of completing them itself, `stoppedCount` counts the sleeps it asked to complete, and the request is best-effort while the run is active. See [`run.wakeUp()` on spec 9 runs](/docs/changelog/spec-9#runwakeup-and-waitforsleep).

## Related functions

- [`start()`](/docs/api-reference/workflow-api/start): Start a new workflow and get its run ID.
Loading
Loading